如何实现 &Struct 的默认值?

Isa*_*aac 5 struct lifetime rust

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

我将我的一段愚蠢的代码简化为这对结构(其中一个引用另一个)。

#[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,
}
Run Code Online (Sandbox Code Playgroud)

如果将此独立代码复制并粘贴到 main.rs 文件中并编译它,则会出现以下错误:

#[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,
}
Run Code Online (Sandbox Code Playgroud)

如果我不注释向量参数生命周期,我会得到预期的结果:“检查你的生命周期男孩”(这是有道理的)。

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
Run Code Online (Sandbox Code Playgroud)

当然,如果我选择更彻底的解决方案,从结构中删除“向量”属性,因为所有其他属性都是标量,则代码可以编译。但我需要我的结构具有某种非标量数据结构。我怀疑我需要在默认初始化程序中为我的向量添加某种生命周期标签,但我不确定什么

顺便说一句,我认为我的TestStructB一生也得到了适当的注释,但也许不是。看起来正确吗?

最后,我到达这里是因为编译器说我需要为原始结构的引用 (&) 版本声明一个默认初始值设定项。这个原始结构已经在我的程序中愉快地使用了,但从未被引用。每当我开始以 &referenced 方式使用它时,Rust 都会声称它需要默认初始值设定项。问题是,我仍然认为在使用引用的结构时我做错了一些事情,并且有一种正确的方法来使用 &referenced(和生命周期注释)结构,而 Rust 不会抱怨它们的(不存在的)默认初始值设定项。

编辑:@JohnKugelman 是非常正确的,在典型的新手与 rust 编译器的战斗试图克服它产生的主要神秘消息之后,我遇到了这种“异常”。

我的简化原版是这样的:

#[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,
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

显然 rustc 误导我认为我需要&Struct默认初始化程序?不能说...

Kev*_*eid 9

出现您不寻常的要求是因为您正在尝试实现包含引用的Default结构。大多数时候,用于具有所有自有值或可以以某种方式为空的类型。TestStructB Default

\n

为了实现Default引用,您必须有一些类型的常量或泄漏值,实现可以返回对 \xe2\x80\x94 引用,基本上需要有一个.&\'a TestStructADefault::default&\'static TestStructA

\n

如果向量是空的,那么这很简单,因为Vec::new()是 a const fn,所以我们可以构造 的完整编译时常量实例TestStructA

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

如果您想在向量中包含一些数据,那么您必须使用延迟初始化机制,例如once_cell能够执行vec!分配向量的内容,然后返回对其的引用。

\n
use once_cell::sync::Lazy;\n\nstatic DEFAULT_A: Lazy<TestStructA> = Lazy::new(|| {\n    TestStructA {byte_1: 10, byte_2: 20, vector: vec![1, 2, 3]}\n});\n\nimpl<\'a> Default for &\'a TestStructA {\n    fn default() -> &\'a TestStructA {\n        &DEFAULT_A\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果你要这样做,我建议也实现,因为有参考值而不是拥有值Default for TestStructA是很奇怪的。Default

\n
impl Default for TestStructA {\n    fn default() -> TestStructA {\n        DEFAULT_A.clone()\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

(这仅在TestStructA也实现时有效Clone,但它可能应该。如果不应该,则将结构文字放入方法中TestStructA::defaultDEFAULT_A定义为Lazy::new(TestStructA::default)。)

\n

然而,所有这些细节都是必要的,因为

\n
    \n
  1. 您正在Default实施TestStructB
  2. \n
  3. 其中始终包含对 a 的引用TestStructA
  4. \n
\n

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

\n