Rust中Subtyping与Variance(&mut不变性)相关的两类编译疑问
问题解答
第一个问题:编译错误产生的原因
错误代码
fn main() { struct Foo<'a, T> { a: &'a mut T, } let p1 = 1; let mut p2 = &p1; { let p3 = 2; let mut p4 = &p3; let mut f = Foo { a: &mut p4, }; f.a = &mut p2; println!("{}", f.a); } println!("{}", p2); }
错误信息
error[E0597]: `p3` does not live long enough --> src/main.rs:10:19 | 10 | let mut p4 = &p3; | ^^^ borrowed value does not live long enough ... 16 | } | - `p3` dropped here while still borrowed 17 | println!("{}", p2); | -- borrow later used here
原因分析
创建Foo实例f时,初始赋值a: &mut p4让编译器推断出Foo的生命周期参数'a至少要覆盖p4的生命周期(也就是内部代码块的范围)。虽然后续我们把f.a改成了&mut p2,但Rust的生命周期推断基于变量声明时的约束,而非后续赋值——f的'a已经被固定为内部代码块的生命周期,这意味着p2被f.a持有的可变引用会被限制在'a范围内。
最后一行println!("{}", p2)尝试使用p2,此时p2仍被f持有的引用(即便已被覆盖)借用,而f的生命周期绑定了p3的生命周期(初始赋值关联了p3),p3在内部代码块结束时就会销毁。编译器判定p2的借用延续到了p3销毁之后,违反了“引用不能超过被引用对象生命周期”的规则,因此报错。
第二个问题:注释最后一行后为何能编译成功
修改后代码
fn main() { struct Foo<'a, T> { a: &'a mut T, } let p1 = 1; let mut p2 = &p1; { let p3 = 2; let mut p4 = &p3; let mut f = Foo { a: &mut p4, }; f.a = &mut p2; println!("{}", f.a); } // println!("{}", p2); }
原因分析
注释掉最后一行后,p2在内部代码块之外不再被使用,编译器可以灵活调整生命周期推断:
- 虽然
f初始绑定了p4的生命周期,但后续f.a被替换为&mut p2,且外部不再引用p2,编译器会将Foo的生命周期参数'a缩小到内部代码块范围。 - 关于
&'a mut T的不变性:这里的不变性针对的是T的类型本身,而非T内部的生命周期。我们修改的是f.a指向的&i32的生命周期(从p3的短生命周期变为p1的长生命周期),而非T的类型(T始终是&i32)。而&i32本身是协变的,因此&'a mut &'long i32可以安全赋值给期望&'a mut &'short i32的位置(只要'long覆盖'a),不会触发不变性限制。 - 内部代码块结束后,
f和p3都会被销毁,所有借用都在被引用对象销毁前结束,符合Rust的借用规则,因此编译通过。
内容的提问来源于stack exchange,提问作者dd.ho
相关产品推荐
相关产品推荐

