Rust lifetime 生命周期
lifetime,生命周期。rust把生命周期提到了语法糖的层面,解决引用-借用问题。
lifetime机制,确保解决所有的borrow借用是有效的。
lifetime和scope有关联,但有差别。
例1,最简单的用法:
//'a 表示一个生命周期,'b也类似 //'a 表示,函数print_refs的生命周期不超过'a,同理,'b也类似。 //也就是说,生命周期'a超过函数print_refs,同理, 'b也是 fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) { println!("x is {} and y is {}", &x, &y); } fn main() { let x = 1; let y = 2; print_refs(&x, &y); }
所有的引用都必须有一个生命周期。
函数的返回值,要么是有生命周期的引用,要么是静态变量。
如果编译器能推导引用的生命周期,那么代码里可以省略生命周期变量。
例2,省略的生命周期:
//下面两个函数如果同名,则函数签名是完全一样的 // 输入参数是引用,生命周期省略了 fn elided_input(x: &i32) { println!("`elided_input`: {}", x); } // 输入参数是引用,生命周期没有省略,效果跟前一个函数是一样的,函数签名也是一样的 fn annotated_input<'a>(x: &'a i32) { println!("`annotated_input`: {}", x); } // 返回值和入参的生命周期可以省略,如果修改为同名,下面两个函数也有相同的函数签名 //返回值和输入参数没生命周期 fn elided_pass(x: &i32) -> &i32 { x } //返回值和输入参数有生命周期 fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x } fn main() { let x = 3; elided_input(&x); annotated_input(&x); println!("`elided_pass`: {}", elided_pass(&x)); println!("`annotated_pass`: {}", annotated_pass(&x)); }
例3,入参出参的生命周期,以及返回值的生命周期
//入参的生命周期'a fn print_one<'a>(x: &'a i32) { println!("`print_one`: x is {}", x); } //可变参数也有生命周期 fn add_one<'a>(x: &'a mut i32) { *x += 1; } //多个入参,各自有不同生命周期,当然也可以设置成相同寿命周期 fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) { println!("`print_multi`: x is {}, y is {}", x, y); } //返回值是引用,也有生命周期 fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x } //这个函数会报错。String::from("foo")在函数体内被创建,scope是这个函数 //函数运行结束,这个变量就要被析构,内存释放。因此,它的引用不能作为返回值。 /* fn invalid_output<'a>() -> &'a String { &String::from("foo") } | ^------------------- | || | |temporary value created here | returns a reference to data owned by the current function */ //fn invalid_output<'a>() -> &'a String { &String::from("foo") } fn main() { let x = 7; let y = 9; print_one(&x); print_multi(&x, &y); let z = pass_x(&x, &y); print_one(z); let mut t = 3; add_one(&mut t); print_one(&t); }
关于Bounds:
T: 'a //T里的所有引用,都可以在'a生命周期之外存活
T: Trait+'a //T实现Trait特质,且T内的所有引用,都在'a生命周期之外存活。
例子4:
use std::fmt::Debug; // Ref是一个tuple,'a是生命周期,'a生命周期比tuple更长 // tuple只有一个元素,这个元素是一个引用,是T的引用,这个引用的生命周期比Ref更长 // T内部的所有引用都在生命周期'a,都超过Ref。 #[derive(Debug)] struct Ref<'a, T: 'a>(&'a T); // 一个打印的范型函数。它的入参是范型T,且范型T必须实现Debug特质。 // 因为实现了Debug特质,这个打印函数根据Debug特质进行打印。 // 这个where指示T的属性 fn print<T>(t: T) where T: Debug { println!("`print`: t is {:?}", t); } // 这个print_ref函数,跟上面的print函数不同之处: // 这里的入参是引用,因此必须有生命周期。 // 生命周期的意思是,T的生命周期是'a,比print_ref生命周期更长,T的所有引用都在'a // +'a 表示 a生命周期一定在print_ref外头 fn print_ref<'a, T>(t: &'a T) where T: Debug + 'a { println!("`print_ref`: t is {:?}", t); } fn main() { //x绑定到栈上的对象i32 7 let x = 7; //ref_x是一个tuple,第一个元素是一个引用,因为有引用,所在定义Ref的时候要有生命周期'a //有生命周期'a,表示第一个元素的引用在Ref的外头,生命周期比Ref长,'a生命周期比Ref长 //包含Ref生命周期 let ref_x = Ref(&x); //入参是引用,所以用print_ref函数打印 //入参是引用,因此函数定义必须有生命周期 print_ref(&ref_x); //常规print,这句可以运行。但如果放到print_ref之前,这句会报错。 print(ref_x); }
例子5,'static也是一种生命周期
// x的bound是T // T是'static + Display特质 //T实现了Display特质,这样函数generic里才能println!宏打印x fn generic<T>(x: T) where T: 'static + std::fmt::Display { println!("x = {}", x); } fn main(){ // A reference with 'static lifetime: //s是静态生命周期变量 let s: &'static str = "hello world"; generic(s); }
例子6,生命周期bound
use std::fmt::Debug; //input的bound:input的生命周期是'static,input也实现Debug特质 fn print_it( input: impl Debug + 'static ) { println!( "'static value passed in is: {:?}", input ); } fn use_it() { // i是'static let i = 5; print_it(i); // oops, &i only has the lifetime defined by the scope of // use_it(), so it's not 'static: // &i的生命周期是use_it函数,所以不是'static,因此这句会报错 print_it(&i); /* 18 | print_it(&i); | ---------^^- | | | | | borrowed value does not live long enough | argument requires that `i` is borrowed for `'static` */ } fn main(){ use_it(); }
在 rust 比较两个对象,检查它们是否相等,应该是哪种相等呢? 比如,一段代码: let mut vec = vec![1, 2, 3];vec.push(4);assert_eq!(vec, [1, 2, ...