为什么Rust编译器无法推断出一个参数的生命周期超过另一个参数?

5

我有以下代码:

struct Solver<'a> {
    guesses: Vec<&'a str>,
}

impl<'a> Solver<'a> {
    fn register_guess(&mut self, guess: &'a str) {
        self.guesses.push(guess);
    }
}

fn foo(mut solver: Solver, guess: &str) {
    solver.register_guess(guess)
}

它无法编译:

   |
11 | fn foo(mut solver: Solver, guess: &str) {
   |        ----------                 - let's call the lifetime of this reference `'1`
   |        |
   |        has type `Solver<'2>`
12 |     solver.register_guess(guess)
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`

错误信息显示参数guess必须比solver存在更久的时间。在我看来,这是显然正确的:函数结束时solver的生命周期结束了,但guess的生命周期却没有结束。这似乎是编译器应该能够推断并且不会出错的内容。
为什么编译器不能这样做呢?这段代码实际上有solver可以比guess存在更长时间的方法吗?还是说编译器根本不尝试进行这种推断?
我知道如何解决这个问题——将函数改成fn foo<'a>(mut solver: Solver<'a>, guess: &'a str)——但我想问的是为什么我必须这样做。
2个回答

6

虽然 solver 本身不能超出 guess 的生命周期,但它所引用的生命周期却可以。例如,使用 Solver<'static> 调用 foo()。这种类型的求解器期望 guess&'static str,并可能将由 guess 引用的数据存储在全局变量中。(请记住,在借用检查 foo() 时编译器不会考虑 register_guess() 做了什么,它只会考虑其签名。)

更一般地说,Solver<'a> 可能包含引用超出 solver 生命周期的 'a 数据。没有任何限制阻止 register_guess() 将 guess 的内容存储在这些引用中。如果不能保证 guess 至少与 'a 生命周期相同,则 foo() 就是不可靠的。例如,考虑以下 Solver 的替代定义:

struct Solver<'a> {
    guesses: &'a mut Vec<&'a str>,
}

如果register_guess()的签名没有更改,那么foo()将允许像这样不安全的代码:

fn main() {
    let mut guesses = vec![];
    let solver = Solver { guesses: &mut guesses };
    {
        let guess = "foo".to_string();
        // stores temporary "foo" to guesses, which outlives it
        foo(solver, guess.as_str());
    }
    println!("{}", guesses[0]); // UB: use after free
}

-1
这个错误来自于Rust的生命周期省略规则。其中一个规则是:

每个参数中省略的生命周期都成为独立的生命周期参数。

Rust会保守地假设每个没有明确指定的生命周期都是不同的。如果您想让某些生命周期相等,就必须显式地指定它。您的问题与一个简单的函数等价,该函数接受两个字符串切片并返回较长的字符串。您必须将这样的函数签名写为fn longer<'a>(&'a str, &'a str) -> &'a str,否则编译器会给出相同的错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接