When I'm delegating work to threads I often have a piece of data that will outlive all of the threads, such as numbers
in the following example:
use std::thread;
fn main() {
let numbers = vec![1, 2, 3];
let thread_a = thread::spawn(|| println!("{}", numbers.len()));
let thread_b = thread::spawn(|| println!("{}", numbers.len()));
thread_a.join().unwrap();
thread_b.join().unwrap();
}
It's not modified anywhere, and because of the join
s, it's guaranteed that the threads are done using it. However, Rust's borrow checker is not able to tell:
error[E0373]: closure may outlive the current function, but it borrows `numbers`, which is owned by the current function
--> src/main.rs:6:34
|
6 | let thread_a = thread::spawn(|| println!("{}", numbers.len()));
| ^^ ------- `numbers` is borrowed here
| |
| may outlive borrowed value `numbers`
|
note: function requires argument type to outlive `'static`
--> src/main.rs:6:20
|
6 | let thread_a = thread::spawn(|| println!("{}", numbers.len()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `numbers` (and any other referenced variables), use the `move` keyword
|
6 | let thread_a = thread::spawn(move || println!("{}", numbers.len()));
| ^^^^^^^
The solutions I've seen so far all involve cloning the piece of data (or cloning an Arc
of the data). Is it possible to do it without any cloning, though?
You might have the wrong idea: cloning an
Arc
is just incrementing a reference counter and making a copy of a pointer; it doesn't perform any additional allocation. Of course, creating theArc
involves an allocation, but then, you're already allocating in order to construct theVec
, so one additional fixed-size allocation isn't likely to hurt.If all you really need is the length, you can just compute that outside the thread's closure and store it in a variable; a
usize
has no problems crossing a thread boundary.The issue is that the compiler is unable to infer from the use of
join()
that a given thread is bound to a limited lifetime... it doesn't even try.Before Rust 1.0, there was a
thread::scoped
constructor that allowed you to pass in non-'static
references, but that had to be de-stabilised due to a memory safety issue. See How can I pass a reference to a stack variable to a thread? for alternatives.