I have this code:
use std::sync::atomic::{AtomicIsize, Ordering};
#[derive(Default)]
pub struct Worker {
work: Vec<u32>,
progress: AtomicIsize,
}
impl Worker {
fn do_work(&mut self) {
self.work.push(0u32);
self.progress.store(self.progress.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
}
fn get_progress(&self) -> isize {
self.progress.load(Ordering::SeqCst)
}
}
pub struct Manager<CB: FnMut()> {
cb: CB
}
impl<CB: FnMut()> Manager<CB> {
fn do_a_bit_more_work(&mut self) {
(self.cb)();
}
}
fn main() {
let mut worker = Worker::default();
let mut manager = Manager {
cb: || worker.do_work()
};
while worker.get_progress() < 100 {
manager.do_a_bit_more_work();
}
}
That is, I have some manager that calls a callback to do some work. I want the callback to be Worker::do_work()
and that function updates the members of Worker
so it needs &mut self
. However once I pass worker.do_work()
to the manager it means worker
is mutably borrowed so I can never use it again.
I want to use it again to check progress, and maybe change its behaviour. I can use atomic operations and mutexes and so on to try to make sure it is safe to do so, but how can I tell Rust to allow this without getting the cannot borrow X as immutable because it is also borrowed as mutable
error?
I'm guessing it is something to do with Cell
or RefCell
but I can't work it out.
Here's the simpler example I'm going to use:
Adding
RefCell
allows it to compile:Now the closure borrows an immutable reference of the
RefCell<Worker>
and checking for an exclusive mutable borrow moves from compile time to runtime.Of course,
RefCell
isn't required to solve the problem, but avoidingRefCell
does mean you have to look at the problem from a different direction. One solution is instead of keeping theWorker
, give it to theManager
. Then borrow it back as needed: