我想编写以下功能:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
*rr1 = *rr2;
}
但是编译器抱怨:
error[E0623]: lifetime mismatch
--> src/lib.rs:2:12
|
1 | fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
| ----------- ------------------- these two types are declared with different lifetimes...
2 | *rr1 = *rr2;
| ^^^^ ...but data from `rr2` flows into `rr1` here
我对Rust的一生的心理模型并不认同该代码是错误的。我将rr2
的类型读取为“寿命为'b
的引用到寿命为'c
到u32
的引用”。因此,当我取消引用rr2
时,会获得一个寿命为'c
的引用至u32
。应该可以安全地存储在具有相同类型的*rr1
中。
如果我要求'b
比'c
长,则可以使用:
fn foo<'a, 'b: 'c, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
*rr1 = *rr2;
}
[这使我认为类型&'b mut &'c mut u32
意味着参考链末端的u32
仅在'b
和'c
的交集期间可用。
这里Rust对行为的正确解释是什么?为何引用的引用以这种方式而不是我以为的方式运行?
您不能取消引用&'b mut &'c mut u32
并获得&'c mut u32
,因为:
&mut
引用不是很容易复制,因此您不能copy &'c mut u32
;和&'c mut u32
(这会使外部参考悬空)。相反,编译器重新借用具有外部生存期u32
的'b
。这就是为什么您会收到一条错误消息,说明来自rr2
的数据流入rr1
。
如果允许编译foo
,则可以使用它来获取对同一&mut
的两个u32
引用,这是引用规则所禁止的:
let (mut x, mut y) = (10, 20);
let mut rx = &mut x;
let mut ry = &mut y;
foo(&mut rx, &mut ry); // rx and ry now both refer to y
std::mem::swap(rx, ry); // undefined behavior!
如果我要求'b胜过'c,则可以使用
因为'c
必须已经超过'b
,所以如果您要求'b
也超过'c
,则得出'c
= 'b
。更新的签名等效于此:
fn foo<'a, 'b>(rr1: &'a mut &'b mut u32, rr2: &'b mut &'b mut u32)
也就是说,您已经统一了'c
和'b
,现在从&'b mut u32
借用rr2
毫无问题,因为内部和外部生命周期都适用于'b
。但是,编译器现在不允许您在我之前给出的示例中编写损坏的代码,因为ry
已在整个生命周期中被借用。
有趣的是,如果将内部引用设为非mut
,它也可以工作:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c u32, rr2: &'b mut &'c u32) {
*rr1 = *rr2;
}
这是因为&
引用是Copy
,所以*rr2
不是重新借用,而实际上只是内部值的副本。有关更多信息,请阅读Why does linking lifetimes matter only with mutable references?