我想编写以下函数:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
*rr1 = *rr2;
}
Run Code Online (Sandbox Code Playgroud)
但是编译器抱怨:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
*rr1 = *rr2;
}
Run Code Online (Sandbox Code Playgroud)
我对 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;
}
Run Code Online (Sandbox Code Playgroud)
这使我认为该类型&'b mut &'c mut u32指u32的交叉点期间,在参考链的末端是唯一可用的'b和'c。
对 Rust 在这里的行为的正确解释是什么?为什么引用的引用会以这种方式而不是我认为的方式?
您不能取消引用 a&'b mut &'c mut u32并获得 a ,&'c mut u32因为:
&mut引用是不可复制的,所以你不能复制&'c mut u32; 和&'c mut u32(这会使外参考悬挂)。相反,编译器reborrows的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!
Run Code Online (Sandbox Code Playgroud)
如果我要求 'b 比 'c 长,它就可以工作
因为'c必须已经超过'b¹,如果你要求它'b也超过'c,那么'c= 'b。更新后的签名相当于:
fn foo<'a, 'b>(rr1: &'a mut &'b mut u32, rr2: &'b mut &'b mut u32)
Run Code Online (Sandbox Code Playgroud)
也就是说,您已经统一了'cand 'b,现在借用 a &'b mut u32from没有问题,rr2因为内部和外部生命周期都为'b. 但是,编译器现在不会让您在我之前给出的示例中编写损坏的代码,因为ry它的整个生命周期都已被借用。
有趣的是,如果您将内部引用设为 non- mut,它也可以工作:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c u32, rr2: &'b mut &'c u32) {
*rr1 = *rr2;
}
Run Code Online (Sandbox Code Playgroud)
这是因为&引用是Copy,所以*rr2不是重新借用,而实际上只是内部值的副本。
有关更多信息,请阅读:
¹当没有明确的界限时,为什么会'c长寿可能并不明显。原因是因为编译器假定类型是well-formed。格式良好可能会变得复杂(请参阅RFC 1214),但在这种情况下,它仅意味着您不能拥有比它所引用的事物()更有效的引用()。'b'c: 'b&'b mut &'c mut u32'b'c