【问题标题】:How lifetime parameters and borrowing interacts in function signatures生命周期参数和借用如何在函数签名中相互作用
【发布时间】:2021-02-21 19:02:10
【问题描述】:

假设我在 Rust 中有一个具有以下签名的函数:

fn f<'a>(x: &'a i32) -> &'a i32;

假设我执行以下操作:

let x = 0;
let y = f(&x);

在这种情况下,Rust 借用检查器认为 y 借用 x。为什么?比“因为你在参数类型和返回类型中使用了相同的生命周期参数”更深层次的原因是什么。

【问题讨论】:

    标签: rust


    【解决方案1】:

    函数签名

    fn f<'a>(x: &'a i32) -> &'a i32;
    

    表示f 返回的值是对x 参数所指内容的引用,因此它不能超过它。例如,这不起作用:

    // Compile error
    let y = {
        let x = 0;
        f(&x)
    
        // x is dropped here
    };
    
    // Here y still "exists", but x doesn't (y outlives x)
    

    针对您的具体问题:

    假设我执行以下操作:

    let x = 0;
    let y = f(&x);
    

    在这种情况下,Rust 借用检查器认为 y 借用 x。为什么?

    答案是因为f 的函数签名告诉了它。举个例子,假设我们把签名改成这样:

    fn f<'a, 'b>(x: &'a i32, z: &'b i32) -> &'a i32;
    

    然后我们像这样调用f

    let x = 0;
    let z = 1;
    let y = f(&x, &z);
    

    在上面的代码中,y 借用了x,但不是z。这是因为f 的返回值有'a 的生命周期与x 的生命周期相同。

    【讨论】:

      【解决方案2】:

      使用 Rustonomicon 中的语法,我们可以详细说明第二个 sn-p

      let x: i32 = 0;
      'a: { // definition of a lifetime (not real syntax)
          let y: &'a i32 = f::<'a>(&'a x) // &'a x is also not real, though interestingly rustc recognizes it
      }
      

      生命周期 'a 由编译器引入,因为您编写了 &amp;,它需要知道借用将持续多长时间(请注意,您不能手动指定借用的生命周期)。函数的类型也意味着y 的类型中有'a。编译器需要找出'a 的开始和结束位置。规则是 'a 必须在您执行借用之前开始,x'a 期间不能移动(因为它是借用的),y 不能在 'a 之后使用(因为它的类型说所以)。如果编译器可以选择 'a 的起点和终点以使规则成立,那么它就会编译。如果不能,则有错误。 xy之间没有直接关系;它经历了他们都与之交互的生命周期'a。例如。你不能移动x然后读*y,因为'a必须在你移动x之前和你读*y之后结束,但是这样的时间不存在。 TL;DR 将借用检查视为生命周期推断。

      【讨论】:

      • 感谢您的解释。我对 rust 如何仅使用生命周期来跟踪可能的别名感到困惑(因为生命周期只关心代码的有效区域),但我现在明白了。借用检查器的所有规则(不仅与悬空引用有关的规则,还有与别名相关的规则)都被转换为生命周期约束。这些生命周期约束形成了一个不等式系统(使用子集关系进行偏序)。如果该系统至少有一个解决方案,则该代码根据借用检查器有效。
      猜你喜欢
      • 1970-01-01
      • 2022-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 1970-01-01
      相关资源
      最近更新 更多