Rust 中的生命周期注解会改变变量的生命周期吗?

Do lifetime annotations in Rust change the lifetime of the variables?

Rust chapter states that the annotations don't tamper with the lifetime of a variable but how true is that? According to the book, the function longest 引用两个字符串,return 引用较长的一个。但是在错误情况下

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

它确实改变了结果变量的生命周期,不是吗?

We’ve told Rust that the lifetime of the reference returned by the longest function is the same as the smaller of the lifetimes of the references passed in.

首先,了解生命周期和作用域之间的区别很重要。引用具有生命周期,这取决于它们引用的变量的范围。

一个变量作用域是词法的:

fn main() {
    let string1 = String::from("long string is long"); // <-- scope of string1 begins here
    let result;
    {
        let string2 = String::from("xyz"); // <-- scope of string2 begins here
        result = longest(string1.as_str(), string2.as_str());
        // <-- scope of string2 ends here
    }
    println!("The longest string is {}", result);
    // <-- scope of string1 ends here
}

当您创建对变量的新引用时,引用的生命周期仅与变量的范围相关联。其他引用附加了不同的生命周期信息,这取决于引用的来源以及在该上下文中已知的信息。当您在类型上放置命名生命周期注释时,类型检查器只是确保附加到任何引用的生命周期信息与注释兼容。

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        // The lifetime of result cannot be longer than `'a` 
        result = longest(string1.as_str(), string2.as_str());
        // But a reference to string2 also has lifetime `'a`, which means that
        // the lifetime `'a` is only valid for the scope of string2 
        // <-- i.e. to here
    }
    // But then we try to use it here — oops!
    println!("The longest string is {}", result);
}

We’ve told Rust that the lifetime of the reference returned by the longest function is the same as the smaller of the lifetimes of the references passed in.

有点。我们 did 将此信息告诉 Rust,但是,借用检查器仍会检查它是否为真!如果它不是 already true 那么我们将得到一个错误。我们无法改变该信息的真实性,我们只能告诉 Rust 我们想要的约束,它会告诉我们是否正确。

在您的示例中,您可以通过更改 longest 上的生命周期注释使 main 函数有效:

fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y // oops!
    }
}

但是现在你得到一个错误inside longest因为它不再符合要求:它现在永远不会对return[=16=有效] 因为它的生命周期可能比 'a 短。事实上,正确实现此功能的唯一方法是:

  1. Return x
  2. Return一片x
  3. Return a &'static str — 因为 'static 比所有其他生命周期都长
  4. 使用unsafe代码

这本书只是在暗示,函数的生命周期参数不能干扰受影响值的生命周期。他们不能使一个值比程序已经规定的值寿命更长(或相反)。

但是,不同的函数签名可以决定这些引用的生命周期。由于引用在其生命周期方面是协变的,因此您可以将 "wider" 生命周期的引用变成该生命周期内更小的引用。

例如,给定定义

fn longest<'a>(a: &'a str, b: &'a str) -> &'a str

,两个输入引用的生命周期必须匹配。但是,我们可以这样写:

let local = "I am local string.".to_string();

longest(&local, "I am &'static str!");

具有'static生命周期的字符串文字与'a生命周期兼容,在这种情况下主要受字符串local.

约束

同样,在上面的例子中,生命周期'a必须被限制在嵌套字符串string2中,否则它不能通过引用传递给函数。这也意味着输出引用受此生命周期限制,这就是为什么当尝试在 string2 范围之外使用 longest 的输出时代码编译失败的原因:

error[E0597]: `string2` does not live long enough
  --> src/main.rs:14:44
   |
14 |         result = longest(string1.as_str(), string2.as_str());
   |                                            ^^^^^^^ borrowed value does not live long enough
15 |     }
   |     - `string2` dropped here while still borrowed
16 |     println!("The longest string is {}", result);
   |                                          ------ borrow later used here

另请参阅此问题以获取对生命周期及其 covariance/contravariance 特征的扩展解释: