Can't put a JoinHandle on struct and then `join()` it because of ownership

I've made an iterator that gets data from crossbeam::Receiver and then should join the thread, by calling .join() on the JoinHandle instance.

I first tried to put .join() into next() method, but it caused an error: "can't move out of self.th which is behind a mutable reference." I tried to put it into drop -- because supposed it should destroy the object, but it made the same error.

struct BgIterator {
	th: thread::JoinHandle<Result<(), BgIterError<OsmObj>>>,
	rc: crossbeam::Receiver<OsmObj>
}

impl Drop for BgIterator {
	fn drop(&mut self) {
		self.th.join();
	}
}

Compilation error:

error[E0507]: cannot move out of `self.th` which is behind a mutable reference
   --> src/main.rs:316:3
    |
316 |         self.th.join();
    |         ^^^^^^^ move occurs because `self.th` has type `JoinHandle<Result<(), BgIterError<OsmObj>>>`, which does not implement the `Copy` trait

No matter what other tricks I played, it always hit this "can't move out..." issue.

  1. Box-ing
  2. having Option<JoinHandle<...>> type and trying to pop the object
  3. calling as_ref on it
  4. calling as_mut (as suggested here)
  5. unpacking into vars
  6. if let Some(th) = self.th.as_mut() { -- from here, same error
  7. naive let (th, self.th) = (self.th, None) -- same error

What's the way to solve this?

Doing self.th.take() (from here) actually does the trick, but is there a more direct way?

[added] A more direct approach I found here, using swap or mem::take.

struct BgIterator {
	th: Option<thread::JoinHandle<Result<(), BgIterError<OsmObj>>>>,
	rc: crossbeam::Receiver<OsmObj>
}
impl Drop for BgIterator {
	fn drop(&mut self) {
		if let Some(th) = self.th.take() {
			th.join();
		}
	}
}
2 Likes