为什么阴影不会释放借来的参考?

时间:2017-08-14 14:05:47

标签: rust

似乎阴影变量不会释放它所持有的借用参考。以下代码无法编译:

fn main() {
    let mut a = 40;
    let r1 = &mut a;
    let r1 = "shadowed";
    let r2 = &mut a;
}

显示错误消息:

error[E0499]: cannot borrow `a` as mutable more than once at a time
 --> src/main.rs:5:19
  |
3 |     let r1 = &mut a;
  |                   - first mutable borrow occurs here
4 |     let r1 = "shadowed";
5 |     let r2 = &mut a;
  |                   ^ second mutable borrow occurs here
6 | }
  | - first borrow ends here

我希望代码能够编译,因为在借用第二个引用r1之前,第一个引用r2被遮蔽了。显然,第一次借用直到阻止结束,尽管在第4行之后不再可以进入。为什么会这样?

2 个答案:

答案 0 :(得分:5)

TL; DR:阴影是关于名字查找,借用是关于生命周期。

从编译器的角度来看,变量没有名称:

fn main() {
    let mut __0 = 40;
    let __1 = &mut __0;
    let __2 = "shadowed";
    let __3 = &mut __0;
}

这对于人类来说不是非常易读,因此该语言允许我们使用描述性名称。

阴影是重用名称的一个限制,对于“阴影”变量的词法范围,它将名称解析为“阴影”(__2此处)而不是“原始”一个(__1这里)。

然而,仅仅因为无法再访问旧版本并不意味着它不再生活 Shadowing!= Assignment 。这在不同的范围内尤其值得注意:

fn main() {
    let i = 3;
    for i in 0..10 {
    }
    println!("{}", i);
}

将始终打印3:一旦阴影变量的范围结束,名称将再次解析为原始版本!

答案 1 :(得分:2)

它不像原来的r1在被遮蔽后不再存在;考虑为您的代码生成的MIR没有最后一行(r2绑定):

fn main() -> () {
    let mut _0: ();                      // return pointer
    scope 1 {
        let mut _1: i32;                 // "a" in scope 1 at src/main.rs:2:9: 2:14
        scope 2 {
            let _2: &mut i32;            // "r1" in scope 2 at src/main.rs:3:9: 3:11
            scope 3 {
                let _3: &str;            // "r1" in scope 3 at src/main.rs:4:9: 4:11
            }
        }
    }

    bb0: {
        StorageLive(_1);                 // scope 0 at src/main.rs:2:9: 2:14
        _1 = const 40i32;                // scope 0 at src/main.rs:2:17: 2:19
        StorageLive(_2);                 // scope 1 at src/main.rs:3:9: 3:11
        _2 = &mut _1;                    // scope 1 at src/main.rs:3:14: 3:20
        StorageLive(_3);                 // scope 2 at src/main.rs:4:9: 4:11
        _3 = const "shadowed";           // scope 2 at src/main.rs:4:14: 4:24
        _0 = ();                         // scope 3 at src/main.rs:1:11: 5:2
        StorageDead(_3);                 // scope 2 at src/main.rs:5:2: 5:2
        StorageDead(_2);                 // scope 1 at src/main.rs:5:2: 5:2
        StorageDead(_1);                 // scope 0 at src/main.rs:5:2: 5:2
        return;                          // scope 0 at src/main.rs:5:2: 5:2
    }
}

请注意,当"shadowed"绑定(_3)时,它不会更改与原始r1绑定相关的任何内容(_2);名称r1不再适用于可变引用,但原始变量仍然存在。

我不认为你的例子是一个非常有用的阴影案例;它的通常应用,例如循环体,更有可能利用它。