我有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();
}
}
也就是说,我有一些经理调用回调做一些工作。我希望回调为Worker::do_work()
,该函数会更新Worker
的成员,因此需要&mut self
。但是,一旦我将worker.do_work()
传递给经理,就意味着worker
被可怜地借用,所以我再也不能使用它了。
我想再次使用它来检查进度,并可能改变它的行为。我可以使用原子操作和互斥体等来尝试确保这样做是安全的,但是如何告诉Rust允许这样做而不会出现cannot borrow X as immutable because it is also borrowed as mutable
错误?
我猜这与Cell
或RefCell
有关,但我无法解决这个问题。
答案 0 :(得分:0)
这是我将要使用的更简单的例子:
struct Manager<F> {
cb: F,
}
impl<F> Manager<F>
where F: FnMut()
{
fn do_a_bit_more_work(&mut self) { (self.cb)() }
}
struct Worker;
impl Worker {
fn do_work(&mut self) {}
fn get_progress(&self) -> u8 { 100 }
}
fn main() {
let mut worker = Worker;
let mut manager = Manager {
cb: || worker.do_work()
};
while worker.get_progress() < 100 {
manager.do_a_bit_more_work();
}
}
添加RefCell
允许它编译:
use std::cell::RefCell;
fn main() {
let worker = RefCell::new(Worker);
let mut manager = Manager {
cb: || worker.borrow_mut().do_work()
};
while worker.borrow().get_progress() < 100 {
manager.do_a_bit_more_work();
}
}
现在,闭包借用了RefCell<Worker>
的不可变引用,并检查从编译时到运行时的独占可变借位。
当然,解决问题不需要RefCell
,但避免RefCell
确实意味着您必须从不同的方向查看问题。一种解决方案是保留Worker
,而不是保留Manager
。然后根据需要借回来:
trait DoWork {
fn do_work(&mut self);
}
struct Manager<T> {
work: T,
}
impl<T> Manager<T>
where T: DoWork
{
fn do_a_bit_more_work(&mut self) {
self.work.do_work()
}
fn inspect<F, U>(&self, mut f: F) -> U
where F: FnMut(&T) -> U
{
f(&self.work)
}
// Optionally
// fn inspect_mut<F, U>(&mut self, mut f: F) -> U
// where F: FnMut(&mut T) -> U
// {
// f(&mut self.work)
// }
fn into_inner(self) -> T {
self.work
}
}
struct Worker;
impl Worker {
fn get_progress(&self) -> u8 {
100
}
}
impl DoWork for Worker {
fn do_work(&mut self) {}
}
fn main() {
let worker = Worker;
let mut manager = Manager { work: worker };
while manager.inspect(|w| w.get_progress()) < 100 {
manager.do_a_bit_more_work();
}
let worker = manager.into_inner();
}