Joshua LI
Joshua LI

Reputation: 4733

Accessing a method of self inside a thread in Rust

I want to propagate the self struct object into a thread and then call its time_tick() method for increasing the HMS time.

pub fn start(&mut self) {
    self.acti = true;   // the time tick is activated now...
    thread::spawn(move || {
        let local_self: *mut Self = self;   // this self live in the thread
        loop {
            thread::sleep(Duration::from_secs(1));  // wait for 1 sec
            if (*local_self).acti == true { (*local_self).time_tick(); }    
            (*local_self).print_time();  // for debug
        }
    });
}

I get the error message:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/hmstimer/mod.rs:42:17
   |
42 |           thread::spawn(move || {
   |  _______________________^
43 | |             let local_self: *mut Self = self;   // this self live in the thread
44 | |             loop {
45 | |                 thread::sleep(Duration::from_secs(1));    // wait for 1 sec
...  |
48 | |             }
49 | |         });
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 40:2...
  --> src/hmstimer/mod.rs:40:2
   |
40 |       pub fn start(&mut self) {
   |  _____^
41 | |         self.acti = true;    // the time tick is activated now...
42 | |         thread::spawn(move || {
43 | |             let local_self: *mut Self = self;   // this self live in the thread
...  |
49 | |         });
50 | |     }
   | |_____^
   = note: ...so that the types are compatible:
           expected &mut hmstimer::HMSTimer
              found &mut hmstimer::HMSTimer
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/hmstimer/mod.rs:42:17: 49:7 self:&mut hmstimer::HMSTimer]` will meet its required lifetime bounds

But it seems that the about method is inappropriate. What is the best practice for doing the task?

Upvotes: 8

Views: 9331

Answers (1)

Francis Gagné
Francis Gagné

Reputation: 65732

You can't pass a closure that captures a mutable reference to thread::spawn. thread::spawn needs the function to be 'static, which means that either it captures no borrows, or that all borrows are 'static. That's because the thread can continue running after the referent has been dropped.

If you don't need to use self in the original thread after calling start, then you can just pass self by value.

pub fn start(self) {
    self.acti = true;
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(1));
            if self.acti == true { self.time_tick(); }    
            self.print_time();
        }
    });
}

Otherwise, you'll need to use Arc to get the two threads to share ownership of Self, as well as Mutex or RwLock to synchronize reads and writes across threads.

// note: this is not a method anymore;
// invoke as `HMSTimer::start(arc.clone());`
pub fn start(this: Arc<Mutex<Self>>) {
    this.lock().expect("mutex is poisoned").acti = true;
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(1));
            let lock = this.lock().expect("mutex is poisoned");
            if lock.acti == true { lock.time_tick(); }    
            lock.print_time();
            // `lock` is dropped here, unlocking the mutex
        }
    });
}

Upvotes: 6

Related Questions