我正在创建一系列包含对较低级别结构的可变引用的数据结构。我一直非常乐意与A
,B
和C
合作,但我尝试添加新图层D
。 A
,B
,C
,D
实际上是用于协议解码的状态机的状态,但我已经删除了所有这些:
struct A {}
fn init_A() -> A {
A {}
}
struct B<'l> {
ed: &'l mut A,
}
fn init_B(mut e: &mut A) -> B {
B { ed: e }
}
struct C<'l> {
pd: &'l mut B<'l>,
}
fn init_C<'l>(mut p: &'l mut B<'l>) -> C<'l> {
C { pd: p }
}
struct D<'lifetime> {
sd: &'lifetime mut C<'lifetime>,
}
fn init_D<'l>(mut p: &'l mut C<'l>) -> D<'l> {
D { sd: p }
}
fn main() {
let mut a = init_A();
let mut b = init_B(&mut a);
let mut c = init_C(&mut b);
// COMMENT OUT THE BELOW LINE FOR SUCCESSFUL COMPILE
let mut d = init_D(&mut c);
}
我收到错误:
error[E0597]: `c` does not live long enough
--> src/main.rs:38:1
|
37 | let mut d = init_D(&mut c);
| - borrow occurs here
38 | }
| ^ `c` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
我完全不了解D
与C
相比,生命周期的变化情况有所不同:我不明白终身不匹配是什么。
答案 0 :(得分:3)
我将解决有关代码无效的原因。
TL; DR:类型C<'l>
和D<'l>
的生命周期的不变性以及对它们使用单个生命周期参数('l
)导致这些类型的变量保持借用只要变量b
存在,但变量c
(由d
借用)在b
之前被删除。
借用检查器本质上是一个约束求解器。它搜索满足各种约束的最短生命期 0 :引用的寿命不得超过它引用的值,生命期必须服从函数签名和类型中指定的约束,并且生命期必须服从方差规则 1
0 - 参考的最短生命周期是最佳的,因为参考不会借用超过必要的值。
1 - Rust有一个variance的概念,它确定是否可以在期望值较小的生命周期的地方使用寿命较长的值。 Rustonomicon链接详细解释了它。
下面的代码是相关代码的简化版本,它失败并出现相同的错误:c
的活动时间不够长。这些块标有变量的生命周期。 'a
是变量a
的生命周期,依此类推。这些生命周期由代码的结构决定,并且它们是固定的。
类型注释(B(&'ar A) -> B<'ar>
等)的生命周期是变量。借用检查器会尝试为这些变量查找固定生命周期('a
,'b
,'c
,“d
)的有效分配。
let
语句下方的评论显示了我将在下面解释的生命周期限制。
struct A;
struct B<'l>(&'l mut A);
struct C<'l>(&'l mut B<'l>);
struct D<'l>(&'l mut C<'l>);
fn main() {
// lifetime 'a
let mut a = A;
{ // lifetime 'b
// B(&'r mut A) -> B<'ar>
let mut b = B(&mut a);
// 'r >= 'ar & 'r <= 'a
{ // lifetime 'c
// C(&'br mut B<'ar>) -> C<'abr>
let mut c = C(&mut b);
// 'br <= 'b & 'abr = 'ar & 'br >= 'abr
{ // lifetime 'd
// D(&'cr mut C<'abr>) -> D<'cabr>
let d = D(&mut c);
// 'cr <= 'c & 'cabr = 'abr & 'cr >= 'cabr
}
}
}
}
首次转让
// B(&'r mut A) -> B<'ar>
let mut b = B(&mut a);
// 'r <= 'a & 'r >= 'ar
对a
的引用不能超过a
,因此'r <= 'a
。
&'r mut A
是'r的变体,因此我们可以将其传递给B<'ar>
的类型构造函数,该构造函数需要&'ar mut A
iff 'r >= 'ar
。
第二次作业
// C(&'br mut B<'ar>) -> C<'abr>
let mut c = C(&mut b);
// 'br <= 'b & 'abr = 'ar & 'br >= 'abr
参考不能超过b
('br <= 'b
),&mut B
对B
('abr = 'ar
)不变,&'br mut B
是'br
的变体1}}('br >= 'abr
)
d
的分配类似于c
。
Rust似乎并没有考虑它作为可能的任务尚未遇到的生命周期。因此,'ar
的可能分配为'a
或'b
,'abr
的分配为'a
,'b
或'c
等等。
这组约束归结为'ar = 'abr = 'cabr
,'ar
允许的最小分配为'b
。因此,b
,c
和d
的类型为B<'b>
,C<'b>
,D<'b>
。也就是说,变量d
在生命周期c
中保留对'b
的引用,但在c
生命周期结束时删除'c
。
如果我们移除d
,那么c
仍然会将b
借用到生命周期结束'b
,但这不是问题,因为{{1} }不会超过生命周期b
。
此说明仍然简化。例如,虽然'b
的类型为c
,C<'b>
并未在整个生命周期c
中借用b
,但它借用了它的一部分'b
在定义'b
之后开始,但我还没有明确的理解。
答案 1 :(得分:2)
原始代码中的init_*()
函数总是返回一个类型,其生命周期参数等于您传入的引用的生命周期。由于您以这种方式构建引用链,因此您的所有生命周期都将结束同样,a
,b
,c
,d
的类型最终会成为A
,B<'a>
,C<'a>
,D<'a>
。这可以在c
之前完成,因为生命周期'a
可以是b
的范围,它可以满足所有约束条件。
但是,一旦将d
添加到混合中,就没有单一的生命周期'a
可以使所有引用都有效。生命周期'a
不再是b
的范围,因为c
的生存时间不够长。它也不是c
的范围,因为它对于b
来说太短了,所以编译器出错了。
通过解耦生命周期,所有变量都有可能拥有自己的生命周期,一切都按预期工作。由于问题仅从D
开始,因此足以在此时引入额外的生命周期。
struct A;
fn init_a() -> A {
A {}
}
struct B<'a> {
ed: &'a mut A,
}
fn init_b(ed: &mut A) -> B {
B { ed }
}
struct C<'b> {
pd: &'b mut B<'b>,
}
fn init_c<'b>(pd: &'b mut B<'b>) -> C<'b> {
C { pd }
}
struct D<'c, 'b: 'c> {
sd: &'c mut C<'b>,
}
fn init_d<'c, 'b: 'c>(sd: &'c mut C<'b>) -> D<'c, 'b> {
D { sd }
}
fn main() {
let mut a = init_a();
let mut b = init_b(&mut a);
let mut c = init_c(&mut b);
let d = init_d(&mut c);
}