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

问题描述 投票:0回答:3

我想写这个结构:

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 文档,但我仍然不知道如何修复它。

rust lifetime
3个回答
85
投票

实际上上面的代码失败的原因不止一个。让我们稍微分解一下并探索一些如何修复它的选项。

首先让我们删除

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
(即,您无法再通过
c1
访问它,只能通过
c
访问它)。这意味着所有对
c1
的引用将不再有效。但是你的
&c1
仍然在范围内(在 B 中),所以编译器不能让你编译这段代码。

当编译器指出类型

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

我们可以通过更改其定义来使

C
可复制,如下所示:

#[derive(Copy, Clone)]
struct C;

现在上面的代码可以运行了。请注意,@matthieu-m 评论仍然是正确的:我们无法在 B 中同时存储对值的引用和值本身(我们在此处存储对值的引用和值的副本) )。但这不仅仅适用于结构,这也是所有权的运作方式。

现在,如果您不想(或不能)使

C

 可复制,您可以将引用存储在 
A
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
 并将其作为参数传入:

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

游乐场


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


2
投票
参加聚会迟到了(从未来回复)并且对 Rust 完全陌生,但我正在到达那里(某种程度上)。建立在这对我有用的答案的基础上,至少是明智的编译。

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

© www.soinside.com 2019 - 2024. All rights reserved.