【问题标题】:Non-lexical lifetimes非词汇生命周期
【发布时间】:2022-11-16 18:03:37
【问题描述】:

有人可以解释以下内容吗?

  1. 这个编译(解释:NLL y在初始定义后没有被引用?)
    fn main() {
        let mut x = 5;
        let y = &x;
        let z = &mut x;
        println!("z: {}", z);
    }
    
    1. 这个没有编译(解释:z未被引用但只引入了之前的行所以仍然有效?)
    fn main() {
        let mut x = 5;
        let y = &x;
        let z = &mut x;
        println!("y: {}", y);
    }
    
    1. 这个编译(解释:NLL z在初始定义后没有被引用?)
    fn main() {
        let mut x = 5;
        let z = &mut x;
        let y = &x;
        println!("y: {}", y);
    }
    
    1. 这个没有编译(只是为了看看引入行是否会导致 z 不被 println 激活)
    fn main() {
        let mut x = 5;
        let y = &x;
        let z = &mut x;
        let foo = String::from("foo");
        println!("y: {}, foo: {}", y, foo);
    }
    

    我很困惑......我在书中找不到任何涵盖此特定案例的内容,但如果有人链接到解释此行为的内容,我将不胜感激。

【问题讨论】:

  • 我怀疑这本书(和语言参考)没有解释这些情况的具体规则的原因是因为语言团队仍在研究检测参考何时不重叠的方法,以使借用检查器更智能;以书面形式规定这种情况发生的确切时间可能为时过早且具有限制性。

标签: rust


【解决方案1】:
  1. fn main() {
        let mut x = 5;
        let y = &x;
        // 'y lifetime starts, holding an immutable reference to x
        // 'y lifetime ends (never used later), releasing hold on x
        let z = &mut x; // this is fine because the hold is released
        println!("z: {}", z);
    }
    
    fn main() {
        let mut x = 5;
        let y = &x;
        // 'y lifetime starts, holding an immutable reference to x
        let z = &mut x; // this is forbidden because of the existing hold
        println!("y: {}", y);
        // 'y lifetime ends, releasing hold on x
    }
    
    fn main() {
        let mut x = 5;
        let z = &mut x;
        // 'z lifetime starts, holding a mutable reference to x
        // 'z lifetime ends (never used later), releasing hold on x
        let y = &x; // this is fine because the hold is released
        println!("y: {}", y);
        // 'y lifetime ends, releasing hold on x
    }
    
    1. 与#2 几乎相同
    fn main() {
        let mut x = 5;
        let y = &x;
        // 'y lifetime starts, holding an immutable reference to x
        let z = &mut x; // this is forbidden because of the existing hold
        let foo = String::from("foo");
        println!("y: {}, foo: {}", y, foo);
        // 'y lifetime ends, releasing hold on x
    }
    

    如您所知,当对某些数据的可变引用也存在时,不能存在其他引用。由于这个原因,你会看到&mut也被称为“独家参考”。

    借用检查器使用代码中引用的生命周期来强制执行此操作。在旧版本的 Rust 中,所有生命周期都是“词法的”——它们的持续时间与它们的包含范围一样长。引入“非词法生命周期”是为了让程序员更轻松,通过使引用生命周期只持续到这些引用被使用的时间。

    这就是允许示例 #1 和 #3 工作的原因。创建了一个引用,但生命周期立即结束,因为它们以后不再使用,因此当下一行创建不同的引用时,生命周期不会重叠。

【讨论】:

    【解决方案2】:

    在这里查看每个变量的生命周期很有用。让我们逐个例子来看。

    // example one
    fn main() {
        let mut x = 5;
        let y = &x;     // y is declared here, but never used
                        // so its lifetime is effectively nil
        let z = &mut x; // by this line, it no longer exists
        println!("z: {}", z);
    }
    
    // example two
    fn main() {
        let mut x = 5;
        let y = &x;      // y is declared here and used in the
                         // println, so...
        let z = &mut x;  // ...this is invalid, since you cannot
                         // take a mutable reference when you have
                         // an existing reference.
        println!("y: {}", y);
    }
    
    // example three
    fn main() {
        let mut x = 5;
        let z = &mut x;  // z is declared here but never used
                         // so its lifetime is effectively nil
        let y = &x;      // by this line, it no longer exists
        println!("y: {}", y);
    }
    
    // example four
    fn main() {
        let mut x = 5;
        let y = &x;
        let z = &mut x;  // This is functionally identical to ex. 2
        let foo = String::from("foo");
        println!("y: {}, foo: {}", y, foo);
    }
    

    【讨论】:

    • 为什么不是示例四中的// z is declared here but never used so its lifetime is effectively nil
    • @AndyJohnson 是,但就像在 2 中一样,它仍然需要声明,并且在 y(不可变引用)存在时采用可变引用是无效的。
    • @AndyJohnson 即使该可变引用立即超出范围,它仍然需要有效才能首先使用它。
    • 有道理,谢谢。我想我假设如果生命周期实际上是零,它会被编译掉
    • @AndyJohnson 我希望如果您删除了let y = &x,那么let z = &mut x 会被注释掉被编译掉。编译器错误发生在此类优化完成之前。
    猜你喜欢
    • 2020-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多