【发布时间】:2021-02-21 19:02:10
【问题描述】:
假设我在 Rust 中有一个具有以下签名的函数:
fn f<'a>(x: &'a i32) -> &'a i32;
假设我执行以下操作:
let x = 0;
let y = f(&x);
在这种情况下,Rust 借用检查器认为 y 借用 x。为什么?比“因为你在参数类型和返回类型中使用了相同的生命周期参数”更深层次的原因是什么。
【问题讨论】:
标签: rust
假设我在 Rust 中有一个具有以下签名的函数:
fn f<'a>(x: &'a i32) -> &'a i32;
假设我执行以下操作:
let x = 0;
let y = f(&x);
在这种情况下,Rust 借用检查器认为 y 借用 x。为什么?比“因为你在参数类型和返回类型中使用了相同的生命周期参数”更深层次的原因是什么。
【问题讨论】:
标签: rust
函数签名
fn f<'a>(x: &'a i32) -> &'a i32;
表示f 返回的值是对x 参数所指内容的引用,因此它不能超过它。例如,这不起作用:
// Compile error
let y = {
let x = 0;
f(&x)
// x is dropped here
};
// Here y still "exists", but x doesn't (y outlives x)
针对您的具体问题:
假设我执行以下操作:
let x = 0; let y = f(&x);在这种情况下,Rust 借用检查器认为 y 借用 x。为什么?
答案是因为f 的函数签名告诉了它。举个例子,假设我们把签名改成这样:
fn f<'a, 'b>(x: &'a i32, z: &'b i32) -> &'a i32;
然后我们像这样调用f:
let x = 0;
let z = 1;
let y = f(&x, &z);
在上面的代码中,y 借用了x,但不是z。这是因为f 的返回值有'a 的生命周期与x 的生命周期相同。
【讨论】:
使用 Rustonomicon 中的语法,我们可以详细说明第二个 sn-p
let x: i32 = 0;
'a: { // definition of a lifetime (not real syntax)
let y: &'a i32 = f::<'a>(&'a x) // &'a x is also not real, though interestingly rustc recognizes it
}
生命周期 'a 由编译器引入,因为您编写了 &,它需要知道借用将持续多长时间(请注意,您不能手动指定借用的生命周期)。函数的类型也意味着y 的类型中有'a。编译器需要找出'a 的开始和结束位置。规则是 'a 必须在您执行借用之前开始,x 在'a 期间不能移动(因为它是借用的),y 不能在 'a 之后使用(因为它的类型说所以)。如果编译器可以选择 'a 的起点和终点以使规则成立,那么它就会编译。如果不能,则有错误。 x和y之间没有直接关系;它经历了他们都与之交互的生命周期'a。例如。你不能移动x然后读*y,因为'a必须在你移动x之前和你读*y之后结束,但是这样的时间不存在。 TL;DR 将借用检查视为生命周期推断。
【讨论】: