上述代码失败的原因其实不止一个.让我们对它进行一点细分,并探索如何修复它的几个选项.
首先,让我们删除new
并try 直接在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
的定义,使其可复制:
#[derive(Copy, Clone)]
struct C;
现在,上面的代码起作用了.请注意,@matthieu-m comments仍然是真的:we can't store both the reference to a value and the value itself in B(我们在这里存储一个值的引用和一个值的副本).不过,这不仅仅是针对 struct ,而是所有权的运作方式.
现在,如果您不想(或不能)使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: B{c: c}}
}
}
playground