如何实现& Struct的默认值? [英] How can I implement Default for &Struct?

查看:81
本文介绍了如何实现& Struct的默认值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在多次阅读锈书之后,我认为我已经开始了解生命周期的要点,但是对我来说,另一个问题是我们需要使用语法来声明它们.我发现这确实违反直觉.

我在这对结构上简化了我的愚蠢代码(一个引用另一个).

 #[派生(调试,默认)]pub struct TestStructA {pub字节_1:u8,pub字节_2:u8,pub vector:Vec u8,}暗示& a TestStructA {fn default()->&'a TestStructA {& TestStructA {字节_1:10,字节_2:20,向量:'a vec![1、2、3]}}}#[派生(调试,默认)]pub struct TestStructB< a>{pub test_array:&'a [u8],pub t_a:&'a TestStructA,} 

如果将隔离的代码复制并粘贴到main.rs文件中并进行编译,则会出现以下错误:

 错误:标签后预期`while`,`for`,`loop`或`{`->src/main.rs:10:59|10 |& TestStructA {字节_1:10,字节_2:20,向量:'a vec![1、2、3]}|^^^预期标签后有while,for,loop或{错误:带标签的表达式后必须带有`:`->src/main.rs:10:59|10 |& TestStructA {字节_1:10,字节_2:20,向量:'a vec![1、2、3]}|--- ^^^^^^^^^^^^^^|||||帮助:在标签后添加`:`|标签|=注意:标签在循环和块之前使用,例如,允许给它们"break"标签 

如果我不注释矢量参数的生存期,则会得到预期的结果:检查您的生存期男孩";(这很有意义).

 错误[E0515]:无法返回对临时值的引用->src/main.rs:10:9|10 |& TestStructA {字节_1:10,字节_2:20,向量:vec![1、2、3]}|^ -------------------------------------------------------------|||||此处创建的临时值|返回对当前函数拥有的数据的引用 

当然,如果我选择一个更严格的解决方案来移除向量",从我的struct属性,因为所有其他属性都是标量,代码会编译.但是我需要我的结构具有某种非标量数据结构.我怀疑我需要在Default初始值设定项中为向量设置某种生命周期标签,但是我不确定是什么

顺便说一句,我认为我的 TestStructB 也是正确的生命周期注释,但也许不是.看起来正确吗?

最后,我到达这里是因为编译器说我需要为原始结构的引用(&)版本声明一个Default初始化程序.这个原始结构已经在我的程序中愉快地使用过,但是从未被引用过.每当我开始以&引用的方式使用它时,Rust都声称它需要其Default初始化程序.问题是我仍然觉得我在使用引用的Struct时做错了什么,并且有一种正确的方法来使用&引用的(且带有生命周期注释)Struct,而Rust不会抱怨它们的(不存在的)默认初始化器./p>

@JohnKugelman绝对不对,我已经谈到了这种畸变"现象.在与rust编译器进行典型的新手斗争之后,试图克服它产生的主要是隐秘的消息.

我的简化原文是:

 #[派生(调试,默认)]pub struct TestStructA {pub字节_1:u8,pub字节_2:u8,pub vector:Vec u8,}#[派生(调试,默认)]pub struct TestStructB< a>{pub test_array:&'a [u8],pub t_a:&'a TestStructA,} 

使用此代码,我得到的错误是:

 错误[E0277]:特征绑定'& TestStructA:默认值'不满足->src/main.rs:11:5|11 |pub t_a:&'a TestStructA,|^&^; TestStructA的特性'Default'没有实现.^^^^^^^^^^^^^^^^^^^^^^^^^^^|=帮助:找到以下实现:< TestStructA作为默认值>=注意:`std :: default :: Default :: default`必需=注意:此错误源于派生宏(在每夜构建中,使用-Z macro-backtrace运行以获取更多信息) 

显然rustc误导了我以为我需要一个& Struct 默认初始值设定项?不能说...

解决方案

出现不寻常的要求是因为您试图为结构 TestStructB 实现 Default 在大多数情况下, Default 用于具有所有拥有值或以某种方式可以为空的类型.

为了为 reference &'a TestStructA 实现 Default,你必须有一些 常量或泄漏值的类型,即 Default :: default 实现可以返回的引用-基本上需要一个&'static TestStructA .

这很容易如果向量为空,因为 Vec :: new() const fn ,因此我们可以构造 TestStructA 的完整编译时常量实例:

  impl<'a>& a TestStructA {fn default() ->&'a TestStructA {静态值:TestStructA = TestStructA {字节_1:10,字节_2:20,向量:Vec :: new()};& VALUE}} 

如果要在向量中包含一些数据,则必须使用类似 使用Once_cell :: sync :: Lazy;静态DEFAULT_A:Lazy< TestStructA>=懒惰的:: new(|| {TestStructA {字节_1:10,字节_2:20,向量:vec![1、2、3]}});暗示& a TestStructA {fn default()->&'a TestStructA {& DEFAULT_A}}

如果您要这样做,我建议为TestStructA实现 Default ,因为对于引用而不是拥有的价值.

  impl TestStructA的默认设置{fn default()->TestStructA {DEFAULT_A.clone()}} 

(这仅在 TestStructA 也实现了 Clone 的情况下有效,但可能应该这样做.如果不行,则将struct文字放入 TestStructA内:: default 方法,并将 DEFAULT_A 定义为 Lazy :: new(TestStructA :: default).)

但是,所有这些详细信息仅是必要的,因为

  1. 您正在为 TestStructB 实现默认,
  2. 总是 包含对 TestStructA 的引用.

在默认情况下,您应该考虑 TestStructB 是否真正需要此引用-如果 t_a 具有类型 Option<&'a TestStructA> ,例如,它可以默认为 None .我没有足够的信息来说明这是否适合您的应用程序-根据这些结构的确切用途做出自己的选择.

After reading the rust book many times I think I'm starting to get the gist of lifetimes, but for me, an additional problem is the syntax we need to use to declare them. I find it is really counterintuitive.

I've simplified a silly code of mine onto this pair of structs (which one referencing the other).

#[derive(Debug, Default)]
pub struct TestStructA {
    pub byte_1: u8,
    pub byte_2: u8,
    pub vector: Vec<u8>,
}

impl<'a> Default for &'a TestStructA {
    fn default() -> &'a TestStructA {
        &TestStructA { byte_1: 10, byte_2: 20, vector: 'a vec![1, 2, 3] }
    }
}

#[derive(Debug, Default)]
pub struct TestStructB<'a> {
    pub test_array: &'a [u8],
    pub t_a: &'a TestStructA,
}

If you copy and paste this isolated code onto a main.rs file and compile it, then you get the following error:

error: expected `while`, `for`, `loop` or `{` after a label
  --> src/main.rs:10:59
   |
10 |         &TestStructA { byte_1: 10, byte_2: 20, vector: 'a vec![1, 2, 3] }
   |                                                           ^^^ expected `while`, `for`, `loop` or `{` after a label

error: labeled expression must be followed by `:`
  --> src/main.rs:10:59
   |
10 |         &TestStructA { byte_1: 10, byte_2: 20, vector: 'a vec![1, 2, 3] }
   |                                                        ---^^^^^^^^^^^^^
   |                                                        | |
   |                                                        | help: add `:` after the label
   |                                                        the label
   |
   = note: labels are used before loops and blocks, allowing e.g., `break 'label` to them

If I don't annotate the vector parameter lifetime, I get the expected: "check your lifetimes boy" (which makes sense).

error[E0515]: cannot return reference to temporary value
  --> src/main.rs:10:9
   |
10 |         &TestStructA { byte_1: 10, byte_2: 20, vector: vec![1, 2, 3] }
   |         ^-------------------------------------------------------------
   |         ||
   |         |temporary value created here
   |         returns a reference to data owned by the current function

Of course, if I choose a more drastic solution as removing the "vector" attribute from my struct, as all other attributes are scalars, the code compiles. But I need my struct to have non-scalar data structures of some kind. I suspect I need some kind of lifetime labeling for my vector inside the Default initializer, but I'm not sure what

By the way, I think my TestStructB is also properly lifetime annotated, but maybe not. Does it look correct?

Finally, I got here because the compiler said I needed to declare a Default initializer for the referenced (&) version of my original struct. This original struct was already happily used in my program, but never referenced. Whenever I started using it in a &referenced fashion, it was Rust claiming it needs its Default initializer. The thing is I still somewhat think I've done something wrong when using my referenced Struct, and that there is a proper way to use &referenced (and lifetime annotated) Structs without Rust complaining about their (non existing) default initializer.

EDIT: @JohnKugelman is more than right, I've come onto this "aberration" after the typical newbie fight with rust compiler trying to overcome the mainly cryptic messages it yields.

My simplified original was this one:

#[derive(Debug, Default)]
pub struct TestStructA {
    pub byte_1: u8,
    pub byte_2: u8,
    pub vector: Vec<u8>,
}

#[derive(Debug, Default)]
pub struct TestStructB<'a> {
    pub test_array: &'a [u8],
    pub t_a: &'a TestStructA,
}

With this code, the error I get is:

error[E0277]: the trait bound `&TestStructA: Default` is not satisfied
  --> src/main.rs:11:5
   |
11 |     pub t_a: &'a TestStructA,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `&TestStructA`
   |
   = help: the following implementations were found:
             <TestStructA as Default>
   = note: required by `std::default::Default::default`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

Apparently rustc misled me into thinking I need a &Struct Default initializer? Can't say...

解决方案

Your unusual requirement arises because you're trying to implement Default for a structure TestStructB containing a reference. Most of the time, Default is used on types that have all owned values or can be empty in some way.

In order to implement Default for a reference &'a TestStructA, you would have to have some constant or leaked value of the type that the Default::default implementation can return a reference to — basically there needs to be an &'static TestStructA.

This is simple to do if the vector is empty because Vec::new() is a const fn, so we can construct a full compile-time-constant instance of TestStructA:

impl<'a> Default for &'a TestStructA {
    fn default() -> &'a TestStructA {
        static VALUE: TestStructA = TestStructA {
            byte_1: 10,
            byte_2: 20,
            vector: Vec::new()
        };
        &VALUE
    }
}

If you want to have some data in the vector, then you would have to use a lazy-initialization mechanism like once_cell to be able to execute vec! to allocate the vector's contents, then return a reference to it.

use once_cell::sync::Lazy;

static DEFAULT_A: Lazy<TestStructA> = Lazy::new(|| {
    TestStructA {byte_1: 10, byte_2: 20, vector: vec![1, 2, 3]}
});

impl<'a> Default for &'a TestStructA {
    fn default() -> &'a TestStructA {
        &DEFAULT_A
    }
}

If you were to do this, I'd recommend also implementing Default for TestStructA, because it is strange to have Default for the reference but not the owned value.

impl Default for TestStructA {
    fn default() -> TestStructA {
        DEFAULT_A.clone()
    }
}

(This only works if TestStructA also implements Clone, but it probably should. If it shouldn't, then instead put the struct literal inside the TestStructA::default method and have DEFAULT_A just be defined as Lazy::new(TestStructA::default).)

However, all these details are only necessary because

  1. You're implementing Default for TestStructB,
  2. which always contains a reference to a TestStructA.

You should consider whether TestStructB actually needs this reference in the default case — if t_a has type Option<&'a TestStructA>, for example, then it can just default to None. I don't have enough information to say whether this is appropriate for your application — make your own choice based on what the exact purpose of these structures is.

这篇关于如何实现&amp; Struct的默认值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆