在 Rust 中使用带有结构的生命周期的正确方法是啥?
Posted
技术标签:
【中文标题】在 Rust 中使用带有结构的生命周期的正确方法是啥?【英文标题】:What is the correct way to use lifetimes with a struct in Rust?在 Rust 中使用带有结构的生命周期的正确方法是什么? 【发布时间】:2015-02-19 17:56:23 【问题描述】:我想写这个结构:
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 <------------------+
这是我尝试过的: 结构 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 中是不可能的。 【参考方案1】:在检查 #rust IRC 上的 Manishearth 和 eddyb 之后,我相信结构不可能存储对自身或自身一部分的引用。所以你想要做的事情在 Rust 的类型系统中是不可能的。
【讨论】:
嗨,Rufflewind,如果无法存储对 struct 本身的一部分的引用,您知道替代方案是什么吗? @nybon 最典型的解决方案是直接引用该值(即上面示例中的self.b.c
,完全省略self.c
),或者如果不希望这样做,则提供一个按需生成对 C 的引用的方法(并且这些引用可以正确地用结构的生命周期进行注释)。【参考方案2】:
实际上上面的代码失败的原因不止一个。让我们稍微分解一下,并探讨一些解决方法。
首先让我们删除new
并尝试直接在main
中构建A
的实例,以便您看到问题的第一部分与生命周期无关:
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
的定义使C
可复制:
#[derive(Copy, Clone)]
struct C;
现在上面的代码可以工作了。请注意,@matthieu-m comments 仍然是正确的:we can't store both the reference to a value and the value itself in 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)
一种可能的解决方案是在new
之外创建C
并将其作为参数传入:
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: Bc: c
playground
【讨论】:
您(或其他任何人)是否知道是否有办法让编译器满意,同时仍在新 fn 中创建“C”? @Sushisource 从技术上讲,您可以返回具有静态生命周期的引用 (&'static C
),但这在实践中很少有用
有谁知道现在是否可以使用Pin
s 来解决这个问题?文档中的示例显示了如何通过将&'a T
替换为*const T
来实现类似的效果。但是,我看不到任何方法可以用原始指针替换B<'a>
。有吗?
在类似的情况下,我最终在这种情况下使用了Arc<>
,这在 IMO 中有点矫枉过正,但我还没有看到更甲壳类动物的做法。
在堆而不是堆栈上分配结构 C 并使用 RC。以上是关于在 Rust 中使用带有结构的生命周期的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章