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

Why can't I assign one dereference of a reference of a reference to another when the outer lifetimes differ?

我想编写如下函数:

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 的类型为 "A reference with lifetime 'b to a reference with lifetime 'c to an u32"。因此,当我取消引用 rr2 时,我得到了一个生命周期为 'cu32 引用。这应该可以安全地存储在具有相同类型的 *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(这会使外部引用悬空)。

相反,编译器 重新借用 具有外部生命周期 'bu32。这就是为什么您会收到一条错误消息,指出来自 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!

If I require that 'b outlives 'c, it works

因为 '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)的引用).