通过例子理解rust生命周期

fn main() {
    let result_ref;
    {
        let num1 = 1;
        let num2 = 2;
        let arg1 = &num1;
        {
            let arg2 = &num2;
            result_ref = snd(arg1, arg2);
            println!("{}", result_ref);
        }
    }
}

fn snd<'a>(_x: &i32, y: &'a i32) -> &'a i32 {
    y
}

正常编译,输出2

fn main() {
    let result_ref;
    {
        let num1 = 1;
        let num2 = 2;
        let arg1 = &num1;
        {
            let arg2 = &num2;
            result_ref = snd(arg1, arg2);
        }
        println!("{}", result_ref);
    }
}

fn snd<'a>(_x: &i32, y: &'a i32) -> &'a i32 {
    y
}

仍然正常编译,输出2,生命周期和 arg2 绑定的作用域无关,因为 arg2 持有的引用指向实际的值 num2,而 num2 此时并没有离开作用域,此时 result_ref 对于 num2 的引用仍然是合法的。

生命周期与持有引用的变量本身的作用域无关

fn main() {
    let result_ref;
    {
        let num1 = 1;
        let num2 = 2;
        let arg1 = &num1;
        {
            let arg2 = &num2;
            result_ref = snd(arg1, arg2);
        }
    }
    println!("{}", result_ref); // UAF occurs here
}

fn snd<'a>(_x: &i32, y: &'a i32) -> &'a i32 {
    y
}
error[E0597]: `num2` does not live long enough
  --> .\test.rs:8:24
   |
5  |         let num2 = 2;
   |             ---- binding `num2` declared here
...
8  |             let arg2 = &num2;
   |                        ^^^^^ borrowed value does not live long enough
...
11 |     }
   |     - `num2` dropped here while still borrowed
12 |     println!("{}", result_ref); // UAF occurs here
   |                    ---------- borrow later used here

error: aborting due to 1 previous error

此时 num2 已经离开作用域被释放,而 result_ref 仍然保留对 num2 的引用,构成 Use-after-free,所以无法编译。


fn main() {
    let result_ref;
    {
        let num2 = 2;
        {
            let num1 = 1;
            result_ref = snd(&num1, &num2);
        }
        println!("{}", result_ref);
    }
}

fn snd<'a, 'b>(_x: &'a i32, y: &'b i32) -> &'b i32 {
    y
}

正常编译

fn main() {
    let result_ref;
    {
        let num1 = 1;
        {
            let num2 = 2;
            result_ref = snd(&num1, &num2);
        }
        println!("{}", result_ref);
    }
}

fn snd<'a, 'b>(_x: &'a i32, y: &'b i32) -> &'b i32 {
    y
}
error[E0597]: `num2` does not live long enough
 --> .\test.rs:7:37
  |
6 |             let num2 = 2;
  |                 ---- binding `num2` declared here
7 |             result_ref = snd(&num1, &num2);
  |                                     ^^^^^ borrowed value does not live long enough
8 |         }
  |         - `num2` dropped here while still borrowed
9 |         println!("{}", result_ref);
  |                        ---------- borrow later used here

error: aborting due to 1 previous error

同样, num2 的生命周期比持有其引用的 result_ref 要短,如果可以在 num2 的生命周期结束后使用 result_ref 会导致 UAF

fn main() {
    let mut result_ref;
    {
        let num1 = 1;
        {
            let num2 = 2;
            result_ref = snd(&num1, &num2);
        }
        result_ref = &num1;
        println!("{}", result_ref);
    }
}

fn snd<'a, 'b>(_x: &'a i32, y: &'b i32) -> &'b i32 {
    y
}

在 num2 的生命周期结束后不使用对 num2 的引用就不会有问题