美文网首页程序员
'static 生命周期约束

'static 生命周期约束

作者: swapmem | 来源:发表于2017-03-22 12:58 被阅读250次

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

    pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>> 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: Hello + 'static + Sized>(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: Hello + 'static + Sized>(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 生命周期约束

        本文链接:https://www.haomeiwen.com/subject/ckylnttx.html