I'm currently experimenting with threads and channels. I've taken the web server thread pool example from the Rust book(2018) and modified it, and now I'm having problems and I'm not sure how to resolve this.
The book, on page 484, creates an FnBox
trait which will allow the application to Box an FnOnce instance and defines a call_box
function which gives the trait the ability to call itself. I modified this so that the closure returns a value:
use std::sync::mpsc;
use std::sync::{mpsc::Sender, Arc, Mutex};
use std::thread;
use std::thread::JoinHandle;
// Added 'type Return'
pub trait FnBox {
type Return;
fn call_box(self: Box<Self>) -> Self::Return;
}
impl<T, F: FnOnce() -> T> FnBox for F {
type Return = T;
fn call_box(self: Box<F>) -> Self::Return {
(*self)()
}
}
type Job<T> = Box<dyn FnBox<Return = T> + Send + 'static>;
So far this is good, I get this. My problem is using this trying to call this closure defined in Job
in spawned threads. I plan to get the return value of the closure and send it through an other channel, but I haven't gotten there yet.
pub struct ThreadPool<T: Send> {
pool: Vec<Worker>,
pub sender: Sender<Job<T>>,
}
impl<T: Send> ThreadPool<T> {
pub fn new(result_sender: &Sender<T>) -> Self {
let (sender, receiver) = mpsc::channel();
let mutex = Arc::new(Mutex::new(receiver));
let mut pool = vec![];
for i in 0..4 {
pool.push(Worker::new(i, &mutex));
}
Self { pool, sender }
}
}
struct Worker {
thread: JoinHandle<()>,
}
impl Worker {
pub fn new<T>(id: usize, receiver: &Arc<Mutex<mpsc::Receiver<Job<T>>>>) -> Self
{
let recv = receiver.clone();
let thread = thread::spawn(move || loop {
//^^^ Error Here: the parameter type 'T' may not live long enough
match recv.lock().as_ref() {
Ok(&mutex_recv) => {
if let Ok(r) = mutex_recv.try_recv() {
let _ = r.call_box();
// ^^^ I plan to use this return value later
}
}
Err(e) => {
eprint!(
"Error occurred trying to get Arc lock in thread {}: {}",
id, e
);
return;
}
}
});
Self { thread }
}
}
So, from what I gather from this error, is that the compiler doesn't know whether the closure will be returning a reference or not. And if the closure is returning a reference then it really needs to know the lifetime involved.
What I want is to tell the compiler is that the closure is giving ownership of the return value to the caller and won't be returning a reference. If I can figure this out I think this will resolve this compile error.
Edit: Here is a link to the playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=093270ff689f6fe810762a2f04ee2fe7