为什么Rust编译器错误为“0”;不能作为不可变借用,因为它也是作为可变借用的;将变量移动到作用域后?

为什么Rust编译器错误为“0”;不能作为不可变借用,因为它也是作为可变借用的;将变量移动到作用域后?,rust,reference,mutable,Rust,Reference,Mutable,在阅读了Rust的作用域和引用之后,我编写了一个简单的代码来测试它们 fn main(){ //1.定义一个字符串 让mut a=String::from(“great”); //2.获取可变引用 设b=&mut a; b、 推_街(“微风”); println!(“b={:?}”,b); //3.A作用域:退出此作用域后c无效 { 设c=&a; println!((“c={:?}”,c); } //4.使用可变引用作为不可变引用的作用域 //不再有效。 println!((“b={:?}”,b

在阅读了Rust的作用域和引用之后,我编写了一个简单的代码来测试它们

fn main(){
//1.定义一个字符串
让mut a=String::from(“great”);
//2.获取可变引用
设b=&mut a;
b、 推_街(“微风”);
println!(“b={:?}”,b);
//3.A作用域:退出此作用域后c无效
{
设c=&a;
println!((“c={:?}”,c);
}
//4.使用可变引用作为不可变引用的作用域
//不再有效。
println!((“b={:?}”,b);//src/main.rs:12:17
|
6 |设b=&mut a;
|----可变借用发生在这里
...
12 |设c=&a;
|^^此处发生不可变借用
...

18 | println!(“b={:?},b);//你的问题是为什么编译器不允许
c
引用已经以可变方式借用的数据。作为一个人,我希望从一开始就不允许这样做

但是-当您注释掉最后一个
println!()
时,代码编译正确。这大概就是导致您得出允许使用别名的结论的原因,“只要可变项不在同一范围内”。我认为该结论不正确,原因如下

的确,在某些情况下,子作用域中的引用允许使用别名,但这需要进一步的限制,例如通过结构投影缩小现有引用的范围。(例如,给定一个
let r=&mut点
,您可以编写
let rx=&mut r.x
,即临时可变借用可变借用数据的子集。)但这里的情况并非如此。这里的
c
是一个全新的共享引用,用于引用
b
已经可变引用的数据。这是绝对不允许的,但它可以编译

答案在于编译器对(NLL)的分析。当您注释掉最后一个
println!()
时,编译器注意到:

  • b
    不是
    Drop
    ,因此,如果我们假装它在最后一次使用后很快就被丢弃了,那么没有人能观察到区别

  • b
    在第一次
    println!()
    之后不再使用

  • 因此,NLL在第一个
    println!()
    之后插入一个不可见的
    drop(b)
    ,从而允许在第一个位置引入
    c
    。这只是因为隐式
    drop(b)
    c
    不会创建可变别名。换句话说,
    b
    的范围被人为地从纯粹的词法分析(其相对于
    {
    }
    的位置)所确定的范围缩短,因此是非词法寿命

    您可以通过将引用包装成一个新类型来检验这一假设。例如,这相当于您的代码,并且它仍然使用最后注释掉的
    println!()
    编译:

    #[derive(Debug)]
    struct Ref<'a>(&'a mut String);
    
    fn main() {
        let mut a = String::from("great");
    
        let b = Ref(&mut a);
        b.0.push_str(" breeze");
        println!("b = {:?}", b);
    
        {
            let c = &a;
            println!("c = {:?}", c);
        }
    
        //println!("b = {:?}", b);
    }
    
    要明确回答您的问题:

    即使在显式地将
    c
    移动到作用域中之后,为什么Rust编译器会产生此错误消息


    因为
    c
    不允许与
    b
    一起存在,不管它是一个内部作用域。允许它存在的情况是,编译器可以证明
    b
    从未与
    c
    并行使用,甚至在构建
    c
    之前就可以安全地删除它。在这种情况下sing是“允许的”,因为尽管
    b
    “在范围内”,但没有实际的别名-在生成的MIR/HIR级别上,引用数据的只是
    c

    您已经失去了“监管链”;
    c
    使
    b
    无效。相反,请执行
    让c=&*b;
    @Shepmaster什么是一个真正的监管链Rust中的“监管链”?这对我来说是一个新术语,在阅读参考文献和范围时,我从未在文档中看到过。请对初学者保持友好的回答。Rust使用的不是真正的术语。
    // causes compilation error for code above
    impl Drop for Ref<'_> {
        fn drop(&mut self) {
        }
    }