如何理解 Rust 中函数参数和 return 值的生命周期?

How to understand the lifetime of function parameters and return values in Rust?

我是 Rust 的新手,我仍在为 Rust 的生活而苦苦挣扎。 Rust 编程语言 书中将生命周期定义为

the scope for which that reference is valid

当上下文是一个函数时,很容易理解。例如,在下面的代码中,s 的生命周期是蓝色框,x 的生命周期是绿色框等。

说到函数,我不太明白函数参数和 return 值的生命周期究竟意味着什么。假设我们有这个功能:

fn parse_record<'i>(input: &'i [u8]) -> Record<'i> { ... }

签名声明 input 参数和 return 值 Record 必须具有相同的生命周期 'i。这是否意味着当我们调用函数时,我们传递给函数的值和 returned 值必须具有相同的生命周期?例如,我可以这样调用 main 函数中的函数:

fn main() {
    let mut v: Vec<u8> = [1_u8, 2_u8, 3_u8].to_vec();
    let result = parse_record(&v);
    // use v and result ...
}

函数签名中的生命周期是否声明 vmain 中的 result 必须具有相同的生命周期?

我想在这里澄清一下,引用Rust by Example

A lifetime is a construct the compiler (or more specifically, its borrow checker) uses to ensure all borrows are valid. Specifically, a variable's lifetime begins when it is created and ends when it is destroyed. While lifetimes and scopes are often referred to together, they are not the same.

Take, for example, the case where we borrow a variable via &. The borrow has a lifetime that is determined by where it is declared. As a result, the borrow is valid as long as it ends before the lender is destroyed. However, the scope of the borrow is determined by where the reference is used.

似乎 Rust 书造成了很多混乱,但是 scopelifetime 确实是不同的东西,当我们谈论拥有数据(而不是借用它)生命周期和范围的简单绑定时匹配在一起。

如果我们有这样简单的代码。 ab 的生命周期将与它们定义的范围相匹配。

fn main() {
    let a = 1;
    let b = 2;
}

在此示例中,贷方 (a) 比借方更早超出范围。

fn main() {

    let b;
    {
        let a = 1;
        b = &a;
    }
    let c = *b;
}

它强制编译器发出错误。


error[E0597]: `a` does not live long enough
 --> src/main.rs:5:9
  |
5 |         b = &a;
  |         ^^^^^^ borrowed value does not live long enough
6 |     }
  |     - `a` dropped here while still borrowed
7 |     let c = *b;
  |             -- borrow later used here

所以这里b的生命周期比a的生命周期长,因为它的作用域更大。请记住,借用范围取决于引用的使用位置。

但是这段代码编译得很好,因为 b 范围在 a 被删除后不会结束。

fn main() {
    let b;
    {
        let a = 1;
        b = &a;
        let c = *b;
    }
}

另一件需要澄清的事情是生命周期语法参考 &'lifetime 的意思。这意味着引用应该与 'lifetime 生命周期一样长。不应该是那么长的一生。

假设 Record 元素是这样定义的。

struct Record<'a> {
   some_member: &'a Type
}

这个签名只是意味着记录的某些成员应该在引用传递给 input 时存在,反之亦然。

fn parse_record<'i>(input: &'i [u8]) -> Record<'i> { ... }

如果我把它翻译成简单的英语。只要 Record 中的字段没有超出范围,传递给函数的引用的贷方就不应超出范围(删除)。

或者 return 函数的值应该与函数的输入参数一样长。

如果我们没有受输入生命周期限制的 return 值,翻译会发生变化。

fn test<'a>(a: &'a i32, b: &'a i32)

这意味着 ab 的贷方应该在范围内,直到函数执行结束。

在许多简单的情况下,编译器会忽略生命周期,您不必担心它们。实际上,在您的示例中,也可以省略生命周期。

fn parse_record(input: &[u8]) -> Record { ...  }

我建议您阅读有关生命周期的 Rust by Example 章节,以便更实际地理解它们。