I'm working through the final project in TRPL
I'm at the part where it's gone from spinning up a thread per request to shipping closures across a channel that are getting pulled by workers in a thread pool and there's this line about how we can't quite yet run the closures, due to things in a Box
needing to be of a known size:
To call a
FnOnce
closure that is stored in aBox<T>
(which is what ourJob
type alias is), the closure needs to move itself out of theBox<T>
because the closure takes ownership ofself
when we call it.
In this line what is self
? The closure that is trying to grab Job
's off the channel is not in a method so it doesn't appear to be an instance of the worker.
impl Worker {
pub fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
let thread = thread::spawn(move || loop {
let job = receiver.lock().unwrap().recv().unwrap();
println!("worker {} got job; executing.", id);
(*job)();
});
Worker { id, thread }
}
}
The original creator of the closure to be run is from the main fn:
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
let pool = ThreadPool::new(4);
for inc in listener.incoming() {
//println!("... NEW REQUEST");
let stream = inc.unwrap();
pool.execute(|| {
handle_connection(stream);
});
}
}
The execute method of the threadpool itself is the "nearest" self that I see to the closure here:
pub fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
But it doesn't appear to actually close over self
(threadpool) itself.
So I'm not sure how to read the above sentence because I can't track what it's talking about.
When I brought this up with a colleague, they pointed out that it's probably the 'self' of this trait method: FnOnce in std::ops - Rust and that while as a user of rust from this closure sugar, I am not implementing call_once
nor explicitly writing (*job.call_once())
, the method is getting called when running (*job)()
.
The other bit is how the solution to this problem seems to point to how the closure needs to be changed from an unknown size to a known size, but the exact means of making it of a known size doesn't quite hit the mark for me. We change it from a Box<FnOnce...>
to a Box<FnBox...>
.
I'm struggling to see here how this level of indirection makes the closure of a known size now. Am I overlooking something mentioned earlier in the book? Otherwise where can I go to read more about how the closure needs to be of a known size and how using aBox
is not enough, where earlier you could use Box
es to resolve this kind of an issue?