fn main() {
let r;
{
let x = 5;
r = &x
}
print!("r:{}", r);
}
let r
这种初始化的方式在RUST
中是被允许的,过程中程序声明了两个变量x
和r
,程序无法正常编译。
x does not live long enough
编译报错的原因:被引用对象的存活时间短语引用者。r
对象的生命周期标注为'a
,x
对象的生命周期为'b
,对象x
会在离开作用域时被销毁,'b
的声明周期要比'a
的声明周期短。
fn main() {
let s1 = String::from("abcd");
let s2 = "xyz";
let result = longest(s1.as_str(), s2);
print!("The longest string is {}", result)
}
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
longest
接收两个字符串切片类型做入参,并返回较长的那一个切片。在main
中调用这个方法验证执行结果。
但程序无法通过编译:missing lifetime specifier
。我们需要给longest
返回类型标注一个生命周期参数,引入RUST
并不确定返回的引用会指向x
还是指向y
。
RUST
中函数体返回一个引用类型,一定也存在引用类型的参数。如果返回的引用没有指向任何参数,那么它可能指向了一个创建于函数内部的值,由于这个值会因为函数结束而离开作用域,所以返回的内容也就变成了悬垂引用。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
声明周期的标注使用一种明显不同的语法:它的参数名称必须以撇号(')开头,且通常使用小写字符。
在函数体后增加<'a>
来声明生命周期标注,声明之后才可以在参数和返回值中使用。单个生命周期的标注本身没有太多意义,标注只所以存在是为了向RUST
描述多个泛型生命周期之间的关系。例子中生命周期的的标注意味着:参数x
、y
和返回值生命周期必须是有交集的。
函数签名表明,函数所获取的两个字符串切片参数的存活时间、以及返回的字符串切片存活时间必须不短于给定的生命周期'a
。记住,当我们在函数签名中指定生命周期参数时,我们并没有改变任何传入值或返回值的声明周期。我们只是向借用检查器指出了一些可以用于检查非法调用的约束。
当我们将具体的引用传入longest
时,被用于替代'a
的具体生命周期就是作用域x
与作用域y
重叠的一部分。泛型生命周期'a
会被具象化为x
与y
两者中声明周期较短的那一个。
&i32 // 引用
&'a i32 // 拥有显示声明周期的引用
&'a mut i32 // 拥有显示声明周期的可变引用
生命周期声明只会出现在引用的类型中,形如&'a
,我们将声明周期参数的标注填写在&
运算符之后,并通过一个空格符来将标注和引用区分开来。
总的来说,如果我们不使用引用,而使用持有自身所有权的数据类型,就不需要做泛型生命周期声明。
结构体的字段也可以声明引用类型,不过需要为结构体定义中的每一个引用都添加生命周期标注。
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please:{}", announcement);
self.part
}
}
如同泛型数据类型一样,为了在结构体定义中使用生命周期参数,我们需要在结构体名称后的尖括号内声明泛型周期参数的名字。这个标注意味这ImportantExcerpt
实例的存活时间不能超过存储在part
中的引用的存活时间。
结构体字段中的生命周期名字总是需要被声明在impl
关键字之后,并被用于结构体名称之后,因为这些生命周期属于结构体类型的一部分。