【问题标题】:Why do I need rebinding/shadowing when I can have mutable variable binding?当我可以拥有可变变量绑定时,为什么我需要重新绑定/阴影?
【发布时间】:2016-11-15 23:07:29
【问题描述】:

当我可以进行可变变量绑定时,为什么我需要重新绑定/隐藏?考虑:

let x = a();
let x = b(x);

对比

let mut x = a();
x = b(x);

可变变量绑定允许对该变量进行可变借用。 但是,与可变绑定相比,阴影是否有一些优势?

【问题讨论】:

    标签: rust


    【解决方案1】:

    因为两者的效果完全不同。


    要真正了解发生了什么,我们需要从头开始:什么是绑定?绑定是什么意思?

    让我们考虑一个简单的函数:fn hello() -> String;

    当像这样调用这个函数时:

    fn main() {
        hello();
    }
    

    会发生什么?

    该函数返回一个String,它会被立即丢弃(执行Drop,因为它正在释放其内存)。

    结果被丢弃是因为它没有绑定变量名,语言规则说如果不绑定则可以立即丢弃1

    但是,如果我们绑定这个结果,我们会延长这个值的生命周期,并且我们可以通过这个绑定访问它......一段时间。

    fn main() {
        let value = hello();
    
        std::mem::drop(value);
    
        println!("{}", value); // Error: moved out of value
    }
    

    是当前的问题:在 Rust 中,值的生命周期独立于绑定的范围。

    一个值甚至不需要在其绑定退出其范围之前被删除:它可以转移到另一个值(类似于从函数返回)。

    fn main() {
        let x;
        {
            let y = hello();
            x = y;
        }
        println!("{}", x);
    }
    

    1如果绑定到_,也会发生同样的情况。


    所以,现在我们已经掌握了绑定和值不同的事实,让我们来看看这两个 sn-ps。

    第一个影子 sn-p,与你的不同:

    fn main() {
        let x = a();
        let x = b();
    }
    

    步骤,按顺序:

    • 表达式a()创建一个值,绑定到x
    • 表达式b()创建一个值,绑定到x
    • b() 创建的值被丢弃
    • a() 创建的值被丢弃

    请注意,x 被重新绑定这一事实不会影响之前绑定的值的生命周期。

    从技术上讲,它的行为与 b() 的结果绑定到 y 完全相同,唯一的例外是之前的 x 绑定在 y 在范围内时不可访问。

    现在,可变的 sn-p:

    fn main() {
        let mut x = a();
        x = b();
    }
    

    步骤,按顺序:

    • 表达式a()创建一个值,绑定到x
    • 表达式b() 创建一个值,该值绑定到x,之前的值(由a() 创建)被删除
    • b() 创建的值被丢弃

    再一次,访问前一个值是不可能的,但是在使用遮蔽时暂时是不可能的(如果在较小的范围内进行遮蔽),使用赋值是永远不可能的,因为值被删除了。

    【讨论】:

    • 一个让灯泡终于亮起来的绝妙答案。 x = y; 函数 fn main() { let x; { let y = hello(); x = y; } println!("{}", x); } 中的 let x = y; 不应该是 let x = y; 另外,您能否向像我这样的新手澄清为什么“b”创造的价值在“a”创造的价值之前被丢弃。试图确保我完全吸收这个。
    • @Kayote:(1)不,我的意思是x = y;let x; 是一种提前声明绑定而不实际绑定它的方法(还)。这在这里用于将值分配给y escape 其范围。 (2) 在词法作用域结束时,变量按其创建的相反顺序被销毁;这是必要的,以便变量可以安全地引用在它之前声明的变量。例如,考虑let vec = vec!(1, 2); let x = &vec[0];:如果vec首先被销毁,x将指向释放的内存!
    • 这是否意味着阴影可能导致某种内存泄漏?在sn-pfn main() { let x = a(); let x = b(); }中,a()创建的值可以一直持续到进程结束,而其他地方根本没有用到。
    • 也许只有我一个人,但这个答案显示了两者有何不同以及它们是如何工作的;我还没有更深入地理解为什么该功能首先内置于语言中,尤其是当您可以轻松说出let x = a(); let y = b(); 而不是使用阴影重新绑定x 两次。
    • @code_dredd:这个答案假设具有相同的名称是可取的,因为在 OP 中重用了相同的名称。是否需要使用阴影而不是仅仅使用不同的名称是所有语言设计者都必须考虑的一个单独问题。虽然我不确定在 StackOverflow 上会如何收到它,但这肯定是我感兴趣的一个问题。
    【解决方案2】:

    我自己找到了一个答案:阴影可以改变变量类型。

    let x = get_some_string();
    let x = x.smart_parse_int();
    

    【讨论】:

    • 不仅如此。您还可以更改可变性(这是常见的用例):let mut foo = Foo::new(); /* build foo */ let foo = foo; // make foo immutable.
    • @ŁukaszNiemier:请注意,您可以简单地使用块表达式,let foo = { let mut foo = Foo::new(); ...; foo };
    猜你喜欢
    • 1970-01-01
    • 2013-09-22
    • 2021-06-25
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    • 2014-06-30
    • 2016-04-09
    相关资源
    最近更新 更多