生锈寿命和借用检查器
rust lifetimes and borrow checker
我正在通读“学习 rust”教程,并试图了解生命周期。 Chapter 10-3 有以下无效示例:
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
下面的段落将错误解释为:
When we’re defining this function, we don’t know the concrete values that will be passed into this function, so we don’t know whether the if case or the else case will execute.
但是,如果我更改代码块以执行其他操作;比如,打印 x 和 return y;这样我们就知道每次 return 编辑的是什么,同样的错误发生了。为什么?
fn longest(x: &str, y: &str) -> &str {
println!("{}", x);
y
}
书上还说:
The borrow checker can’t determine this either, because it doesn’t know how the lifetimes of x and y relate to the lifetime of the return value.
我的疑惑是:
- 借用检查器是否能够跨函数跟踪生命周期?如果有,能否举个例子?
- 我不明白这个错误。 x 和 y 是传递给
longest
的引用,因此编译器应该知道它的所有者在别处(并且它的生命周期将持续到 longest
之后)。当编译器发现 return 值不是 x 就是 y 时,为什么生命周期会出现混淆?
将函数想象成一个黑盒子。你和编译器都不知道里面发生了什么。您可能会说编译器“知道”,但事实并非如此。想象一下,它 returns X 或 Y 基于远程 HTTP 调用的结果。它怎么能提前知道?
但它需要提供一些保证,以确保返回的引用可以安全使用。这是通过强制您(即开发人员)明确指定输入参数和返回值之间的关系来实现的。
首先您需要指定参数的生命周期。我将对 x 使用 'x
,对 y 使用 'y
,对结果使用 'r
。因此我们的函数看起来像:
fn longest<'x, 'y, 'r>(x: &'x str, y: &'y str) -> &'r str
但这还不够。我们仍然需要告诉编译器这些关系是什么。有两种方法可以做到(魔术语法将在后面解释):
- 在
<>
括号内:<'a, 'b: 'a>
- 在这样的
where
子句中:where 'b: 'a
两个选项相同,但如果您有大量通用参数,where 子句将更具可读性。
回到问题。我们需要告诉编译器 'r
依赖于 'x
和 'y
并且只要它们有效它就会有效。我们可以通过说 'long: 'short
来做到这一点,这意味着“lifetime 'long 必须至少与 lifetime 'short 一样长”。
因此我们需要像这样修改我们的函数:
fn longest<'x, 'y, 'r>(x: &'x str, y: &'y str) -> &'r str
where
'x: 'r,
'y: 'r,
{
if x.len() > y.len() {
x
} else {
y
}
}
即我们是说我们的返回值不会超过函数参数的寿命,从而防止出现“释放后使用”的情况。
PS:在这个例子中你实际上可以只用一个生命周期参数来做,因为我们对它们之间的关系不感兴趣。在这种情况下,生命周期将是 x/y:
中较小的一个
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str
我正在通读“学习 rust”教程,并试图了解生命周期。 Chapter 10-3 有以下无效示例:
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
下面的段落将错误解释为:
When we’re defining this function, we don’t know the concrete values that will be passed into this function, so we don’t know whether the if case or the else case will execute.
但是,如果我更改代码块以执行其他操作;比如,打印 x 和 return y;这样我们就知道每次 return 编辑的是什么,同样的错误发生了。为什么?
fn longest(x: &str, y: &str) -> &str {
println!("{}", x);
y
}
书上还说:
The borrow checker can’t determine this either, because it doesn’t know how the lifetimes of x and y relate to the lifetime of the return value.
我的疑惑是:
- 借用检查器是否能够跨函数跟踪生命周期?如果有,能否举个例子?
- 我不明白这个错误。 x 和 y 是传递给
longest
的引用,因此编译器应该知道它的所有者在别处(并且它的生命周期将持续到longest
之后)。当编译器发现 return 值不是 x 就是 y 时,为什么生命周期会出现混淆?
将函数想象成一个黑盒子。你和编译器都不知道里面发生了什么。您可能会说编译器“知道”,但事实并非如此。想象一下,它 returns X 或 Y 基于远程 HTTP 调用的结果。它怎么能提前知道?
但它需要提供一些保证,以确保返回的引用可以安全使用。这是通过强制您(即开发人员)明确指定输入参数和返回值之间的关系来实现的。
首先您需要指定参数的生命周期。我将对 x 使用 'x
,对 y 使用 'y
,对结果使用 'r
。因此我们的函数看起来像:
fn longest<'x, 'y, 'r>(x: &'x str, y: &'y str) -> &'r str
但这还不够。我们仍然需要告诉编译器这些关系是什么。有两种方法可以做到(魔术语法将在后面解释):
- 在
<>
括号内:<'a, 'b: 'a>
- 在这样的
where
子句中:where 'b: 'a
两个选项相同,但如果您有大量通用参数,where 子句将更具可读性。
回到问题。我们需要告诉编译器 'r
依赖于 'x
和 'y
并且只要它们有效它就会有效。我们可以通过说 'long: 'short
来做到这一点,这意味着“lifetime 'long 必须至少与 lifetime 'short 一样长”。
因此我们需要像这样修改我们的函数:
fn longest<'x, 'y, 'r>(x: &'x str, y: &'y str) -> &'r str
where
'x: 'r,
'y: 'r,
{
if x.len() > y.len() {
x
} else {
y
}
}
即我们是说我们的返回值不会超过函数参数的寿命,从而防止出现“释放后使用”的情况。
PS:在这个例子中你实际上可以只用一个生命周期参数来做,因为我们对它们之间的关系不感兴趣。在这种情况下,生命周期将是 x/y:
中较小的一个fn longest<'a>(x: &'a str, y: &'a str) -> &'a str