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 对象比较 equal

在 rust 比较两个对象,检查它们是否相等,应该是哪种相等呢? 比如,一段代码: let mut vec = vec![1, 2, 3];vec.push(4);assert_eq!(vec, [1, 2, ...