神秘的借用范围扩展

10
为什么编译器会拒绝这段代码:
struct S<'a> {
    i: i32,
    r: &'a i32,
}

fn main() {
    let mut s = S{i: 0, r: &0};
    {
        let m1 = &mut s;
        m1.r = &m1.i;
    }
    let m2 = &mut s;
}
错误信息是:"不能同时将` s `借为可变引用超过一次"(第一次借用: `m1`,第二次借用: `m2`)。
为什么在 `m1` 超出作用域之后, `s` 的第一个借用仍然存在?
我了解到有借用范围延伸的情况,但这通常涉及到原始借用者范围之外的另一个借用者“接管”了原始借用对象。例如,下面的代码会产生完全相同的错误,这对我来说非常明显:
fn main() {
    let mut s = 0;
    let r: &mut i32;
    {
        let m1 = &mut s;
        r = m1;
    }
    let m2 = &mut s;
}

在第一个示例中,如果我将m1.r = &m1.i;替换为m1.r = &dummy;(其中dummy定义为某个&i32)或者let dummy = &m1.i;,代码就可以编译。只有当我将借用结构体的另一个字段中的引用存储到另一个字段中时,才会出现错误。我不明白为什么这会延长borrow的范围。

我对代码存在问题的最佳猜测是:

  • s.r的原始生命周期是整个main函数,

  • 当我将引用分配给m1.r时,它必须是原始生命周期,但是&m1.i只在m1存活期内有效。

但我可能是错的(错误消息可能会误导人)。

1个回答

6
首先注意,
let mut s = S{i: 0, r: &0};
{
    s.r = &s.i;
}
let m2 = &mut s;

提供
cannot borrow `s` as mutable because `s.i` is also borrowed as immutable

希望这一点很清晰 - 如果一个结构体自己借用了,则它被借用。 这就说明为什么任何自我借用的结构基本上是无用的 - 它不能移动(使其自身指针无效),也不能对其进行可变引用。
接下来需要理解的是,从可变引用中获取不可变引用会计入到可变引用中的借用次数,因此将其延伸。例如:
let mut v = ();
let r1 = &(&mut v);
let r2 = &v;

提供

cannot borrow `v` as immutable because it is also borrowed as mutable

目前不清楚这是否可以合法地成为原始结构的新借款,但它尚未起到这样的作用。


有趣的部分是嵌套的作用域:在我的例子中,借用者m1超出了作用域。为什么你认为borrow的范围和borrower的范围相同?借用者作用域的唯一重要方面是它不能大于借用的范围,并且任何对它的借用(例如&mut &mut T)都受到它的限制。它所持有的借用的生命周期是无限的-考虑 let x: &'static str = "foo" - Veedrac
为什么你认为我做了那个假设?我试图通过使用“borrow scope”而不是“lifetime”的术语来明确说明,例如在这里解释的那样。当然,借用范围可以大于借用者的范围,但前提是在第一个借用者的范围之外有另一个借用者。 - dacker
@dacker:“但在这种情况下不是这样的:m1是在m2之前唯一的借用者。”→ 不,m1.r =&m1.i;导致sm1的借用中借用,因此ss可变地借用。这显然将持续s的生命周期。 - Veedrac
@dacker 我想真正的问题是,为什么你期望 m1.r = &m1.i; 不会强制增加 m1 的借用生命周期。显然它必须这样做。 - Veedrac
你说得对,那正是我期望的结果。我只看到m1.rm1.i中借用,没有从s中进一步借用。我原本以为 Rust 编译器不会进行代码分析,因此无法“看到”m1.i实际上是s.i(就像你所说的是s的借用)。 - dacker
显示剩余3条评论

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