使用线程时的生命周期问题

Lifetime woes when using threads

我很难编译:

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() {}

据我所知,生命周期是正确的,但我遇到了错误...

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 || {
   |         ^^^^^^^^^^^^^

我不明白错误指的是什么生命周期 - 闭包体的生命周期? - 或者为什么它们必须比静态寿命长。

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

这是为什么?由于 Foo 包含借位,因此它的有效期很短。如果 Rust 允许您将 Foo 的实例发送到另一个线程,那么该线程可以很容易地在它借用的数据变得无效后很长时间内使用 Foo

您可能认为这实际上限制过多,您是对的。没有理由不允许 local 并行性,只要您可以向借用检查器证明线程在某个生命周期内终止即可。目前 Rust 中没有结构可以做到这一点,但是有一些未来的解决方案可以解决这个问题,例如 this RFC 扩展了 Send 特性以允许本地并行性。