使用线程时终生难过

Shu*_*hum 6 closures lifetime rust

我很难编译这个:

use std::thread::{self, JoinHandle};

struct Foo<'c> {
    foo: &'c str,
}

impl<'c> Foo<'c> {
    fn use_in_another_thread<F>(self, mut cb: F) -> JoinHandle<Foo<'c>>
        where F: FnOnce(&mut Foo),
              F: Send
    {
        thread::spawn(move || {
            cb(&mut self);
            self
        })
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

据我所知,生命是健全的,但我收到了错误......

error[E0477]: the type `[closure@src/main.rs:12:23: 15:10 cb:F, self:Foo<'c>]` does not fulfill the required lifetime
  --> src/main.rs:12:9
   |
12 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^
   |
   = note: type must outlive the static lifetime

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:14:13
   |
14 |             self
   |             ^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 11:4...
  --> src/main.rs:11:5
   |
11 |       {
   |  _____^ starting here...
12 | |         thread::spawn(move || {
13 | |             cb(&mut self);
14 | |             self
15 | |         })
16 | |     }
   | |_____^ ...ending here
note: ...so that expression is assignable (expected std::thread::JoinHandle<Foo<'c>>, found std::thread::JoinHandle<Foo<'_>>)
  --> src/main.rs:12:9
   |
12 |           thread::spawn(move || {
   |  _________^ starting here...
13 | |             cb(&mut self);
14 | |             self
15 | |         })
   | |__________^ ...ending here
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `Foo<'_>` will meet its required lifetime bounds
  --> src/main.rs:12:9
   |
12 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

我不明白错误所指的生命周期 - 封闭体的寿命? - 或者为什么他们必须比静态寿命更长寿.

ree*_*eem 8

导致此问题的生命周期约束是一个Thread::spawn,需要FnOnce闭包Send.Send要求'static,这意味着数据不包含非'static数据.您的数据,Foo包含借来的str,这不'static,这使得Foo'static.因此,您无法Foo跨线程发送.

为什么是这样?由于Foo包含借用,它仅在很短的一生中有效.如果Rust允许您将实例发送Foo到另一个线程,那么该线程可能很容易Foo在其借用的数据变为无效后很长时间使用.

您可能认为这实际上过于严格,而且您是对的.只要你能向借用检查器证明线程在某个生命周期内终止,就没有理由不允许局部并行.Rust目前没有构造可以做到这一点,但是这个问题有一些未来的解决方案,比如这个RFC扩展了Send特性以允许本地并行.