E0597: rust生命周期
1. 错误代码
# Exp. 1.
- Right:
fn main() { let str1 = "hello"; let longer; { let str2 = ", world"; longer = longest(str1, str2); } println!("{}", longer); } fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
- Wrong:
fn main() { let str1 = String::from("hello"); let longer; { let str2 = String::from(", world"); longer = longest(str1.as_str(), str2.as_str()); } println!("{}", longer); } fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
# Exp. 2.
- Right:
fn main() { let x = &1; let z; { let y = &2; z = larger(x, y); } println!("{}", z); } fn larger<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { if x > y { x } else { y } }
- Wrong:
fn main() { let x = 1; let z; { let y = 2; z = larger(&x, &y); // 临时变量,即只在本行存在的变量,在函数中表达的生命周期跟随原始变量。 } println!("{}", z); } fn larger<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { if x > y { x } else { y } }
2. 解读
Exp. 1.
- snip11中的代码可以通过编译,因为"hello"是&str类型,因而我们无法在程序中得到owner,所以它是'static周期,所以输入的两个变量的生命周期都是一样的'static(猜测是这样)
- snip12中的代码无法通过编译,因为.to_str()返回的&str是一个临时变量,在函数中代表的生命周期是和调用者String的生命周期是相同的,所以两个参数生命周期不同错误。
Exp. 2.
- snip21中的代码不能通过编译,因为1,和2被赋值后他们的所有者的生命周期是可以确定的,而临时变量在函数中代表的生命周期是和调用者i32的生命周期是相同的,所以两个参数生命周期不同错误。
- snip22中的&1,和&2的所有者的生命周期是和snip11中的&str是一样的,解释也是一样的。所以通过。
并且,原文中有这么一段,也许有帮助:
编译器采用三条规则来判断引用何时不需要明确的标注。第一条规则适用于输入生命周期,后两条规则适用于输出生命周期。如果编译器检查完这三条规则后仍然存在没有计算出生命周期的引用,编译器将会停止并生成错误。这些规则适用于
fn
定义,以及impl
块。第一条规则是每一个是引用的参数都有它自己的生命周期参数。换句话说就是,有一个引用参数的函数有一个生命周期参数:
fn foo<'a>(x: &'a i32)
,有两个引用参数的函数有两个不同的生命周期参数,fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
,依此类推。第二条规则是如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:
fn foo<'a>(x: &'a i32) -> &'a i32
。第三条规则是如果方法有多个输入生命周期参数并且其中一个参数是
&self
或&mut self
,说明是个对象的方法(method)(译者注: 这里涉及 Rust 的面向对象,参见第 17 章), 那么所有输出生命周期参数被赋予 self 的生命周期。第三条规则使得方法更容易读写,因为只需更少的符号。