当使用多个嵌套作用域时会发生什么?

4
fn main() {
    let mut m = 12;
    {
        let n = &mut m;
        *n = 13;
        {
            let k = n;
            *k = 20;
            println!("{}", k);
        } // k's scope ends here, right?
        println!("{}", n);
    }
    println!("{}", m);
}

当我运行此代码时,得到以下结果:

src/main.rs:11:18: 11:19 error: use of moved value: `n` [E0382]
src/main.rs:11      println!("{}", n);
                               ^

但是变量k的作用域已经结束了吗?为什么没有将所有权归还给变量n


@Shepmaster 谢谢您 - user3099695
2
这是一个有趣的问题,但不幸的是标题几乎没有用...虽然多个作用域确实很重要,但这里的重要问题是所有权转移及其缺乏“反向转移”...但我看不到任何方法来表达这一点,以便下一个想了解此问题的人有机会找到这个问题 :/ - Matthieu M.
1个回答

8
但是变量k的作用域已经结束了吗?为什么它没有将所有权归还给变量n呢?
是的,k的作用域已经结束了,但你为什么认为它应该归还所有权呢?
在Rust中,没有东西可以“归还所有权”。如果类型没有实现Copy(而且&mut引用绝对没有),那么它的值只能被移动。移动意味着所有权转移,因此由所有权接收者决定如何处理该值。因此,当k超出作用域时,指针就被“销毁”了(指针本身而不是值)。由于它已从n中移出,绑定实际上变为未初始化状态,因此您会得到此错误。
&mut引用是独特的,虽然它们不能复制,因此只能移动,但有时它们会自动重新借用,也就是说,编译器会自动为您插入&mut *p。我不记得自动重新借用适用的确切规则(据我所记,当可变引用传递给函数时以及可能在其他地方发生),但这不是这种情况。要使代码正常工作,您需要显式重新借用该值:
fn main() {
    let mut m = 12;
    {
        let n = &mut m;
        *n = 13;
        {
            let k = &mut *n;  // explicit referencing of the dereferenced value
            *k = 20;
            println!("{}", k);
        }
        println!("{}", n);
     }
     println!("{}", m);
}

这样编译器就知道n没有被移动到k中,允许你在k的作用域结束后继续使用它。

顺便提一句,由于自动重新借用,以下内容(感谢Veedrac提醒)也可以工作:

fn main() {
    let mut m = 12;
    {
        let n = &mut m;
        *n = 13;
        {
            let k: &mut _ = n;  // automatic reborrowing because of type annotation
            *k = 20;
            println!("{}", k);
        }
        println!("{}", n);
     }
     println!("{}", m);
}

看起来如果编译器知道目标类型是一个可变引用,它将重新借用原始引用。否则,也就是说,如果目标类型是未知的(例如在通用上下文中或在没有显式类型注释的绑定上进行分配时),引用将被移动而不是重新借用。因此,在可变引用周围的行为可能会有些令人困惑。


你说“绝对不会出现这种情况”,但我认为这并不是那么明确。例如,let k: &mut _ = n; 是重新借用。如果未来编译器的更改也将其变成重新借用,我也不会感到惊讶。 - Veedrac
嗯,确实你说得对。我忘记了这一点,谢谢! - Vladimir Matveev

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