当无法推断 Rust 借用检查器中的生命周期时?

Jon*_*Lee 3 lifetime rust borrow-checker

在大多数情况下,Rust 编译器可以推断生命周期。如果生命周期范围是在运行时确定的,则表示必须显式标记生命周期。

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

这里,

  • 生命周期是通用的。
  • 这意味着在函数结果返回后,存在一个绑定到生命周期 'a 的范围。
  • 编译器可以知道内存在最小生命周期“a”内有效的信息。

我很好奇。编译器不能不使用生命周期语法,而是采用生命周期 'a 可以绑定的较少范围区域吗?

fn main() { //larger scope
    let s1 = String::from("long string is long");

    { //fewer scope
        let s2 = String::from("xyz");
        let result = longest(s1.as_str(), s2.as_str());
        println!("The longest string is {}", result); 
    } 
      
}
Run Code Online (Sandbox Code Playgroud)

即使调用方的调用堆栈更复杂,作用域区域也是在借用时确定的,所以同样的问题似乎是可能的。

fn func1<'a>(x: &'a str, y: &'a str) {
   let c = String::from("hello");
   let result = func2 (a,b,c)
   ...
}
Run Code Online (Sandbox Code Playgroud)

use*_*342 5

我认为你处理这个问题的方式是错误的。当编译诸如main()您所示的函数时,借用检查器不会检查它调用的各个函数的内容longest(),例如,它只检查它们的签名。这是一个功能:它允许更改函数的实现,而不会影响其签名提供的保证。如果main()成功编译,您可以确定,只要您longest不更改其声明,无论您如何修改,它都会继续编译。

在 的情况下longest(),未注释的签名是不明确的:

fn longest(x: &str, y: &str) -> &str

// does the above mean:
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str // returns sub-slice of x
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'b str // returns sub-slice of y
fn longest<'c>    (x: &'c str, y: &'c str) -> &'c str // returns sub-slice outlived by x and y
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'static str  // returns static data
Run Code Online (Sandbox Code Playgroud)

如果没有生命周期注释,我们无法仅通过查看声明来判断返回的值是&str来自第一个&str、第二个&str、公共生命周期还是可能来自静态&str。对于非常简单的函数,例如那些接受并返回单个引用的函数,编译器会执行“生命周期省略”,它会机械地选择未注释签名的“明显”解释,从而允许您编写fn longest(x: &str) -> &str为 的简单简写fn longest<'a>(x: &'a str) -> &'a str。但是当一个函数接受多个引用时,编译器拒绝猜测并让你拼出你想要的内容。

正如答案开头所指出的,这常常让初学者感到困惑,编译器绝对拒绝做的是从函数体推断生命周期签名,因为这会使签名依赖于实现。