I'm trying to write a function parallelize
that takes as an argument a function B
and returns a function C
, where C
returns a handle to a thread which is running B
. I understand this may be clunky or ridiculous; it's mostly as a learning project. The higher level goal is to implement a parallel map function on iterators, where each element of the list will be mapped in a different thread (or potentially through a thread pool eventually). I have quickly realized that I'm in way over my head. Given what I have described, what should the type of parallelize
be?

- 388,571
- 95
- 1,107
- 1,366

- 400
- 3
- 14
1 Answers
Let's step through it piece by piece:
a function
parallelize
fn parallelize() {
unimplemented!()
}
Yes, that's a function alright.
argument a function B
I assume you'd like the function to be able to return something, so I'll use the generic type R
.
fn parallelize<B, R>(function: B)
where
B: FnOnce() -> R,
{
unimplemented!()
}
returns a function C
Uh oh. You can't do this cleanly in Rust yet. That means we will return a boxed trait object instead
fn parallelize<B, R>(function: B) -> Box<FnOnce()>
where
B: FnOnce() -> R,
{
unimplemented!()
}
where C returns a handle to a thread which is running B
use std::thread::JoinHandle;
fn parallelize<B, R>(function: B) -> Box<FnOnce() -> JoinHandle<R>>
where
B: FnOnce() -> R,
{
unimplemented!()
}
And there's your signature. All done here!
.....
Of course, there's more to do. For example, you didn't state when the thread should actually start. That means I get to pick, and I pick the easiest way — the thread is started when the caller asks for the handle.
use std::thread::{self, JoinHandle};
fn parallelize<B, R>(function: B) -> Box<FnOnce() -> JoinHandle<R>>
where
B: Send + FnOnce() -> R + 'static,
R: Send + 'static,
{
Box::new(|| thread::spawn(function))
}
I guess you could start it immediately, as well:
use std::thread::{self, JoinHandle};
fn parallelize<B, R>(function: B) -> Box<FnOnce() -> JoinHandle<R>>
where
B: Send + FnOnce() -> R + 'static,
R: Send + 'static,
{
let handle = thread::spawn(function);
Box::new(|| handle)
}
I don't know why you'd do the second one; in that case you might as well just return the handle directly. And if you do that, you might as well just call thread::spawn
directly.
Oh, I suppose I couldn't slip those extra trait bounds past you, could I? When calling thread::spawn
, you have to ensure that nothing you pass it will stop being valid before the thread exits (thus 'static
) and is safe to transfer between threads (thus Send
).
The higher level goal is to implement a parallel map function on iterators
That exists and is called Rayon.
See also:
- How can I pass a reference to a stack variable to a thread?
- Lifetime of variables passed to a new thread
- How do I use static lifetimes with threads?
- Cannot call a function in a spawned thread because it "does not fulfill the required lifetime"
- Lifetime woes when using threads
- Thread references require static lifetime?
- "the type does not fulfill the required lifetime" when using a method in a thread

- 388,571
- 95
- 1,107
- 1,366