rust - 我如何安全地使用可变借用的对象?

标签 rust immutability borrow-checker

我有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 blored as mutable 错误?

我猜这与 CellRefCell 有关,但我无法解决。

最佳答案

这是我将要使用的更简单的示例:

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();
}

关于rust - 我如何安全地使用可变借用的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41266071/

相关文章:

syntax - 使用多个匹配项时,是否有更简单的方法来绑定(bind)整个匹配项?

rust - 使用 Rocket 日志时在此范围内找不到宏 `log`

java - 如何创建一个交换可变类型的方法? (自然数)Java

java - 与不可变对象(immutable对象)一起使用的类工厂 (java) 方法的调用方法

rust - 对不可变数组切片的引用的逆序

rust - 如何从使用rust 的集合中返回 Vec<String>?

rust - 是否可以通过委托(delegate)给结构成员来创建实现 Ord 的宏?

sql - 数据库对不可变字段的支持

rust - 为什么不能在同一结构中存储值和对该值的引用?

iterator - 我是否错误地实现了 IntoIterator 作为引用,或者这是一个应该报告的 Rust 错误?