在Rust中使用struct的生命周期的正确方法是什么?

时间:2014-12-21 11:47:07

标签: rust lifetime

我想写这个结构:

struct A {
    b: B,
    c: C,
}

struct B {
    c: &C,
}

struct C;

B.c应该从A.c借用。

A ->
  b: B ->
    c: &C -- borrow from --+
                           |
  c: C  <------------------+

这是我试过的:     struct C;

struct B<'b> {
    c: &'b C,
}

struct A<'a> {
    b: B<'a>,
    c: C,
}

impl<'a> A<'a> {
    fn new<'b>() -> A<'b> {
        let c = C;
        A {
            c: c,
            b: B { c: &c },
        }
    }
}

fn main() {}

但它失败了:

error[E0597]: `c` does not live long enough
  --> src/main.rs:17:24
   |
17 |             b: B { c: &c },
   |                        ^ borrowed value does not live long enough
18 |         }
19 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'b as defined on the method body at 13:5...
  --> src/main.rs:13:5
   |
13 |     fn new<'b>() -> A<'b> {
   |     ^^^^^^^^^^^^^^^^^^^^^

error[E0382]: use of moved value: `c`
  --> src/main.rs:17:24
   |
16 |             c: c,
   |                - value moved here
17 |             b: B { c: &c },
   |                        ^ value used here after move
   |
   = note: move occurs because `c` has type `C`, which does not implement the `Copy` trait

我已阅读有关所有权的Rust文档,但我仍然不知道如何修复它。

2 个答案:

答案 0 :(得分:35)

上面的代码失败实际上有多个原因。让我们分解一下,然后探讨如何修复它的几个选项。

首先让我们删除new并尝试直接在A中构建main的实例,这样您就会发现问题的第一部分无关终生:

struct C;

struct B<'b> {
    c: &'b C,
}

struct A<'a> {
    b: B<'a>,
    c: C,
}

fn main() {
    // I copied your new directly here
    // and renamed c1 so we know what "c"
    // the errors refer to
    let c1 = C;

    let _ = A {
        c: c1,
        b: B { c: &c1 },
    };
}

这失败了:

error[E0382]: use of moved value: `c1`
  --> src/main.rs:20:20
   |
19 |         c: c1,
   |            -- value moved here
20 |         b: B { c: &c1 },
   |                    ^^ value used here after move
   |
   = note: move occurs because `c1` has type `C`, which does not implement the `Copy` trait

它说的是,如果您将c1分配给c,则会将其所有权移至c(即您无法通过{{1}访问它},只能通过c1)。这意味着对c的所有引用都将不再有效。但是你的c1仍然在范围内(在B中),所以编译器不能让你编译这段代码。

当编译器声明类型&c1不可复制时,编译器会在错误消息中提示可能的解决方案。如果您可以复制C,那么您的代码就会有效,因为将C分配给c1会创建值的新副本,而不是移动原始副本的所有权

我们可以通过更改其定义来使c可复制:

C

现在上面的代码可行了。请注意,@matthieu-m comments仍为真:we can't store both the reference to a value and the value itself in B(我们在此处存储对值的引用和值的COPY)。但是,这不仅仅是针对结构,而是所有权的运作方式。

现在,如果您不想(或不能)#[derive(Copy, Clone)] struct C; 可复制,则可以在CA中存储引用。< / p>

B

一切都好吗?不是......我们仍然希望将struct C; struct B<'b> { c: &'b C, } struct A<'a> { b: B<'a>, c: &'a C, // now this is a reference too } fn main() { let c1 = C; let _ = A { c: &c1, b: B { c: &c1 }, }; } 的创建移回A方法。而这就是我们将终生遇到麻烦的地方。让我们将new的创建移回方法:

A

如预期的那样,这是我们的终身错误:

impl<'a> A<'a> {
    fn new() -> A<'a> {
        let c1 = C;
        A {
            c: &c1,
            b: B { c: &c1 },
        }
    }
}

这是因为在error[E0597]: `c1` does not live long enough --> src/main.rs:17:17 | 17 | c: &c1, | ^^ borrowed value does not live long enough ... 20 | } | - borrowed value only lives until here | note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1... --> src/main.rs:13:1 | 13 | impl<'a> A<'a> { | ^^^^^^^^^^^^^^ error[E0597]: `c1` does not live long enough --> src/main.rs:18:24 | 18 | b: B { c: &c1 }, | ^^ borrowed value does not live long enough 19 | } 20 | } | - borrowed value only lives until here | note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1... --> src/main.rs:13:1 | 13 | impl<'a> A<'a> { | ^^^^^^^^^^^^^^ 方法的末尾销毁c1,因此我们无法返回对它的引用。

new

一种可能的解决方案是在fn new() -> A<'a> { let c1 = C; // we create c1 here A { c: &c1, // ...take a reference to it b: B { c: &c1 }, // ...and another } } // and destroy c1 here (so we can't return A with a reference to c1) 之外创建C并将其作为参数传递:

new

playground

答案 1 :(得分:3)

在#rust IRC上与Manishearth和eddyb核实后,我认为结构不可能存储对自身或其自身的一部分的引用。所以你想要做的事情在Rust的类型系统中是不可能的。