函数参数等的生命周期
rust保证内存安全的手段之一是没有悬空的引用(指针),而这个靠编译期间强制生命周期检查实现。先给出生命周期的声明方式:
&i32 // i32的引用
&'a i32 // 带有显式声明周期的引用
&'a mut i32 // 带有显式生命周期的可变引用
考虑函数一个函数(该函数不会通过编译):
fn longer(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
假设该函数能通过编译,而且在如下情景使用:
let x = String::from("foo");
let z;
{
let y = String::from("hello world !");
z = longer(x, y);
}
println!("{} is longer", z);
上述代码还会报错的,因为z
的引用在离开作用域后,就失效了,println
宏内部使用了一个悬空引用。
想要保证绝对内存安全,需要让返回的引用的有x
和y
中最短的生命周期,给出实际的实现方式:
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这个声明,意味着返回的&str
有x
和y
中最短的生命周期。这种的强制声明,可以检测出之前代码段的缺陷。
使用’a
的方式,可以显式声明生命周期。生命周期语法是用于将函数的多个参数与其返回值的生命周期进行关联的。一旦他们形成了某种关联,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或是违反内存安全的行为。
结构体也可以采用类似的方式,代码实例:
struct Duck<'a, 'b, 'c> {
name: &'a str,
nick_name: &'b str,
address: &'c str,
}
则Duck
结构实例化对象的生命周期一定是小于等于name
、nick_name
和address
中最小的那个。
省略生命周期
给出两个概念:
- 输入生命周期:函数的输入
- 输出生命周期:函数的返回值
三个省略规则:
- 每一个是引用的参数都有它自己的生命周期参数。换句话说就是,有一个引用参数的函数有一个生命周期参数:
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
,那么self
的生命周期被赋给所有输出生命周期参数。这使得方法更容易读写,因为只需更少的符号
直接给出代码实例:
fn print(s: &str); // elided
fn print<'a>(s: &'a str); // expanded
fn debug(lvl: u32, s: &str); // elided
fn debug<'a>(lvl: u32, s: &'a str); // expanded
// In the preceding example, `lvl` doesn’t need a lifetime because it’s not a
// reference (`&`). Only things relating to references (such as a `struct`
// which contains a reference) need lifetimes.
fn substr(s: &str, until: u32) -> &str; // elided
fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded
fn get_str() -> &str; // ILLEGAL, no inputs
fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs
fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is ambiguous
fn get_mut(&mut self) -> &mut T; // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded
fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided
fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded
fn new(buf: &mut [u8]) -> BufWriter; // elided
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a>; // expanded
结构体的生命周期
如果结构体的生命周期与外界的相关联,那么结构体的方法也需要声明对应的生命周期,直接给出代码实例:
struct Duck<'a, 'b, 'c> {
name: &'a str,
nick_name: &'b str,
address: &'c str,
}
impl<'a, 'b, 'c> Duck<'a, 'b, 'c> {
fn foo(&self) {}
}
fn main() {}
静态生命周期
let s: &'static str = "I have a static lifetime.";
在整个程序中都是可用的。