Is there any trick to move `&mut self` among threads without `'static` lifetime bound

This troubled me for 2 years:

use std::thread;

struct Adder {
    v: i32,
}

impl Adder {
    fn add(&mut self, x: i32) {
        let jh = thread::spawn(move || {
            self.v += x;
        });
        jh.join().unwrap();
    }
}

Quite easy and wrong, ahahaha.

borrowed data escapes outside of method
  --> src\lib.rs:9:18
   |
8  |       fn add(&mut self, x: i32) {
   |              ---------
   |              |
   |              `self` is a reference that is only valid in the method body
   |              let's call the lifetime of this reference `'1`
9  |           let jh = thread::spawn(move || {
   |  __________________^
10 | |             self.v += x;
11 | |         });
   | |          ^
   | |          |
   | |__________`self` escapes the method body here
   |            argument requires that `'1` must outlive `'static`

I know this is due to thread::spawn's signature asking for a 'static lifetime.

pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,

And I do know it is not necessary to spawn a job to do such an add.

But I really think it cannot lead a bug if compiler allows this, and I do want to make this complile without RefCell, AtomicI32, Mutex and so on.

Facts:

  • I cannot have multi &mut self
  • add spawns only one thread, and block until it finishing, as if only running on main thread
  • I only have one mut ref, I cannot spawn mutil add jobs

No possibility to lead a trace condition.

So, is there any trick to do so or whether I omit anything important?

scoped threads do not have a 'static bound as they implicitly wait for all spawned threads within the scope to finish before continuing control flow of the spawning thread. That guarantees that &mut self isn't dangling when the spawned thread tries to access it.

use std::thread;

struct Adder {
    v: i32,
}

impl Adder {
    fn add(&mut self, x: i32) {
        thread::scope(|s| {
          s.spawn(move || {
            self.v += x;
          });
        });
    }
}

Playground.

4 Likes

Thank a lotttttttttttttttttttttttttttttttt!

And I know in my condition:

  • if I join in add, it's completely unnecessary
  • if not, it leads to race condition

Maybe it's good to improve the compile error info for others' trying what I did. I'll open the first issue to rust's repo in my life right now.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.