当返回对一个可变值的不可变引用,而该可变值位于作用域之外时,为什么在作用域结束时可变引用会被丢弃?

10
fn main() {
    // block1: fails
    {
        let mut m = 10;

        let n = {
            let b = &&mut m;
            &**b // just returning b fails
        };

        println!("{:?}", n);
    }

    // block2: passes
    {
        let mut m = 10;

        let n = {
            let b = &&m;
            &**b // just returning b fails here too
        };

        println!("{:?}", n);
    }
}
块1 出现以下错误:
error[E0597]: borrowed value does not live long enough
  --> src/main.rs:7:22
   |
7  |             let b = &&mut m;
   |                      ^^^^^^ temporary value does not live long enough
8  |             &**b // just returning b fails
9  |         };
   |         - temporary value dropped here while still borrowed
...
12 |     }
   |     - temporary value needs to live until here

我假设在block2作用域之外,内部的不可变引用得以延长,而在block1中,即使存在对其的外部引用,内部的可变引用也总是被丢弃了吗?

1个回答

6
在这里,我们可以将可变借用想象成一个非Copy结构体(S),它拥有所引用值的所有权。这个模型代表了可变借用的独占性质。
根据这个模型进行推理:在block2中,n是对原始m的引用,而在block1中,n最终会成为持有可变借用所有权的m副本的引用。在两个block中,内部引用在let-block结束时被丢弃,但只有在block1中才会出现问题,因为在block1中,当内部引用被丢弃时,n的目标引用仍然由内部引用拥有所有权。
struct S { m: i32 }
let mut m = 10;

let n = {
    let s = S { m };
    let b = &s;
    &(*b).m
}; // s is dropped

println!("{:?}", n);

在上面的代码片段中,s 拥有 m 的副本所有权。引用 n 将指向该副本,当 s 被删除时,该副本也会被删除 - 不被允许。如果 m 是非 Copy 类型,则 m 将移动到 s 中,这将具有相同的影响。
在 block2 中,原始的 m 直接被借用而不是复制。如果强制进行复制,您将得到与 block1 相同的错误:
let mut m = 10;

let n = {
    let m2 = m;
    let mut a = &m2;
    let b = &a;
    &**b
};

println!("{:?}", n);

block1 中触发 m 被复制的是什么?我的意思是,在这种情况下 `fn main() { let mut m:i32 = 10; let n = { let b = &mut m; *b = 12; &*b };println!("{:?}", n);}`,它并不是。是因为外部引用触发了复制吗? - soupybionics
一个临时的可变引用将始终获取它所指向的值的所有权(复制/移动)吗? - soupybionics
1
我可以看到&mutS之间的相似之处,但这不可能是全部。首先,如果您删除额外的&层,则[block1将起作用](http://play.rust-lang.org/?gist=80efb0bfa11cccf7add174435a74c4c5&version=stable&mode=debug&edition=2015),而使用`S`的版本仍将无法编译。 - trent
只有一个 &(即 &mut)的 block1 能够工作,因为它没有涉及到复制(即没有所有权),也没有临时变量(类似于 rvalue?)。所以我想这就是为什么我问是否所有权被占用,因为它是一个临时引用(在 &&mut 的情况下)。 - soupybionics
@soupybionics 显然,“S”过于简单,无法模拟“&mut”的完整语义。我猜测外部引用强制编译器基于更严格的“&mut”模型进行推理,这个模型更接近“S”,因此不能保证代码的安全性,而在缺少外部引用的情况下,编译器可以充分利用其对“&mut”的知识来推断代码确实是安全的。 - Calculator
@计算器 "temporary value does not live long enough" 是指 &mut m 而不仅仅是 m(根据错误消息在 &mut m 下面有 ^^^^^^ 标记整个 &mut m 而不仅仅是 m 的猜测)。因此,&mut 是临时值,指向 m 而不获取所有权(无移动/复制)。如果 &mut 获取所有权,那么此代码不应该编译通过才对? 但它通过了。 - soupybionics

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