I've been coding in Rust for some time but I am often caught off-guard by these nuances. For example, this works:
use std::thread::JoinHandle;
fn main() {
let all_threads: Vec<JoinHandle<()>> = vec![];
for t in all_threads {
t.join().unwrap();
}
}
This doesn't (error: cannot move out of *t
which is behind a shared reference)
fn main() {
let all_threads: Vec<JoinHandle<()>> = vec![];
all_threads.iter_mut().for_each(move |t| t.join().unwrap());
}
I was under the impression that the snippet above was syntactic sugar for the one below, but clearly I am missing something. Can someone help me how does the for loop move instances out?
for
loops in Rust are actually a syntactic sugar for using the IntoIterator
trait, not IterMut
, as used in your example. You can see the exact details on how the desugaring is done exactly in the Rust docs.
Knowing that, we can change the example from your question so that it compiles now:
use std::thread::JoinHandle;
fn main() {
let all_threads: Vec<JoinHandle<()>> = vec![];
all_threads.into_iter().for_each(move |t| t.join().unwrap());
}
The reason your original code doesn't work is the subtle difference in borrowing semantics. IterMut::Iterator
implementation yields &mut T
(mutable references to T
), while IntoIterator::IntoIter
yields T
objects (owned objects). Since JoinHandle::join()
requires self
(i.e. it consumes the object), it won't work with a mutable reference returned by IterMut
.