为什么在外部生命周期不同时,我不能将一个引用的引用的取消引用之一分配给另一个?

5
我想编写以下函数:
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行为背后的正确解释是什么?为什么引用的引用会以我认为的方式而非另一种方式表现?
1个回答

10

无法对&'b mut &'c mut u32进行解引用并获得一个&'c mut u32,因为:

  • &mut引用不是可以轻易复制的,因此不能复制&'c mut u32;而且
  • 无法移出引用,因此也不能移动&'c mut u32(这会使外部引用悬空)。

相反,编译器会使用带有外部生命周期'b重新借用u32。这就是为什么会出现错误消息,说明来自rr2的数据流入了rr1

如果foo被允许编译,那么您可以使用它来获取两个指向同一个u32&mut引用,这违反了引用规则:

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统一起来,现在可以从rr2中借用&'b mut u32而不会出现问题,因为内部和外部生命周期都存在于'b中。然而,编译器现在不允许您编写我之前给出的示例中的错误代码,因为ry已经被借用了整个生命周期。

有趣的是,如果使内部引用变为非mut,它也会起作用:

fn foo<'a, 'b, 'c>(rr1: &'a mut &'c u32, rr2: &'b mut &'c u32) {
    *rr1 = *rr2;
}

这是因为&引用是Copy,所以*rr2不是重新借用,而只是内部值的副本。

欲了解更多信息,请阅读:


¹ 如果没有明确的'c: 'b绑定,'c为何超出'b的范围可能并不明显。原因是编译器假设类型&'b mut &'c mut u32良构的。良构性可能变得复杂(请参见RFC 1214),但在这种情况下,它仅意味着您不能拥有一个引用,它的有效期超过了它所引用的东西('c)的有效期('b)。


我一直在寻找隐式重新借用的参考链接,你有吗?似乎 Rust 参考手册中没有提到它,但是“因为隐式重新借用”已经成为近年来许多问题的答案。 - Sven Marnach
@SvenMarnach很不幸,如果我有的话,我会链接到它(我可能还有一些旧答案需要更新!)我认为它也不在Nomicon中。 - trent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接