'static 生命周期约束

Rust标准库创建线程的函数有如下签名:

pub fn spawn(self, f: F) -> io::Result> where
        F: FnOnce() -> T, F: Send + 'static, T: Send + 'static

可以看到,spawn函数对泛型参数FT都有Send + ’static约束,先不讨论这里的Send 约束,重点关注对泛型参数FT’static生命周期约束。看下面的一个例子:

use std::thread;

fn test() {
    let x = &666;
    let _ = thread::spawn(move|| x).join();
}

fn main() {
    test();
}

这个例子不能通过编译。编译器提示:

error: borrowed value does not live long enough

在函数test中有一个借用类型的栈变量x,我们预期能把它move到一个新创建的线程中,但是编译器阻止了我们的动作。这是因为Rust标准库创建的新线程是与创建它的父线程是detach模式的,也就是说,子线程脱离了父线程的运行环境;当test函数运行完毕时,借用型变量x的生命周期结束,然而我们新创建的线程在test函数运行完毕时不一定被调度执行。这时,如果x被move进新线程,这可能会引起use-after-free的内存安全问题。Rust是强调内存安全的语言,她会在编译时就阻止我们这样做。

编译器为什么能这么智能的阻止我们的这种不安全的行为呢?这里对泛型参数'F'T'static生命周期约束起了关键作用。'static生命周期约束从字面意思上理解,由她限定的类型在整个程序运行的过程中都不会被析构。或者另外一种理解:创建新线程时的closure参数在捕获外部变量的时候,如果外部变量是引用类型,或者外部变量内部包含引用类型,那么这些引用类型的生命周期应该不小于'static

正如参考链接1中所说:

The 'static bound on a type doesn't control how long that object lives; it controls the allowable lifetime of references that object holds.

对某一类型的'static约束不是用来控制这个类型的对象可以存活多久;'static约束控制的是这个类型的对象所包含的引用类型所允许的生命周期。 看下面这个例子:

struct TestStruct(i32);
trait Hello: Sized {
    fn say_hello(&self) {
        println!("hello");
    }
}

impl Hello for TestStruct{}

fn test1(t: &T) {
    t.say_hello();
}

fn test2() {
    let x = TestStruct(666);
    test1(&x);
}

fn main(){
    test2();
}

这个例子中,test2函数有一个栈变量xxtest2调用结束时就会被释放,它的生命周期并不是程序运行的整个过程。x的引用被传递到test1函数中。尽管对类型T'static约束,但是程序是可以正常运行的,即x没有违背对它的'static约束,否则程序不会编译通过。我们再看一个类型中含有引用类型的例子:

struct TestStruct<'a>(&'a i32);
trait Hello: Sized {
    fn say_hello(&self) {
        println!("hello");
    }
}

impl<'a> Hello for TestStruct<'a>{}

fn test1(t: &T) {
    t.say_hello();
}

fn test2() {
    let y = 666;
    let x = TestStruct(&y);
    test1(&x);
}

fn main(){
    test2();
}

正如我们预期的那样会出现编译错误

error: y does not live long enough

当去掉test函数中对类型T'static约束时,程序编译运行正常,输出hello

至此,总算明白了'static生命周期约束在Rust中的作用。总结起来就是参考链接中提到的两点:

  • 'static生命周期约束不是用来控制对象的存活时间;
  • 'static生命周期约束的是对象里包含的引用变量的生命周期;

Reference:

  1. https://users.rust-lang.org/t/why-does-thread-spawn-need-static-lifetime-for-generic-bounds/4541
  2. http://rustbyexample.com/scope/lifetime/static_lifetime.html

你可能感兴趣的:('static 生命周期约束)