下面的代码有效。它懒惰地评估xys并分别缓存到Foo::x: CellFoo::ys: RefCell中。

但是,我认为可能会有更好的方法。我不喜欢必须包装CacheVecGuard,以便在调用站点上可以使用self.borrow_ys()代替冗长的&self.ys.borrow().1

我如何改善这段代码?

在这种情况下,是否有任何适合进行懒惰评估或内存的规范摘要? (我知道lazy_static不合适)

use std::cell::{RefCell, Cell, Ref};
use std::ops::Deref;

struct CacheVecGuard<'a>(Ref<'a, (bool, Vec<f64>)>);

impl<'a> Deref for CacheVecGuard<'a> {
    type Target = [f64];

    fn deref(&self) -> &Self::Target {
        &(self.0).1
    }
}

fn pre_calculate_x(x: f64) -> f64 {
    x
}

fn pre_calculate_ys(x: f64, ys: &mut [f64]) {
    for i in 0..ys.len() {
        ys[i] += 1.0;
    }
}

struct Foo {
    pub a: f64,
    x: Cell<Option<f64>>,
    ys: RefCell<(bool, Vec<f64>)>,
}

impl Foo {
    pub fn new(a: f64) -> Self {
        Self {
            a,
            x: Cell::new(None),
            ys: RefCell::new((false, vec![0.0; 10])),
        }
    }

    fn get_x(&self) -> f64 {
        match self.x.get() {
            None => {
                let x = pre_calculate_x(self.a);
                self.x.set(Some(x));
                println!("Set x to {}", x);
                x
            }
            Some(x) => x,
        }
    }

    fn borrow_ys(&self) -> CacheVecGuard {
        {
            let (ref mut ready, ref mut ys) = *self.ys.borrow_mut();
            if !*ready {
                pre_calculate_ys(self.a, ys);
                println!("Set ys to {:?}", ys);
                *ready = true;
            }
        }
        CacheVecGuard(self.ys.borrow())
    }

    fn clear_cache(&mut self) {
        *(&mut self.ys.borrow_mut().0) = false;
        self.x.set(None);
    }

    pub fn test(&self) -> f64 {
        self.borrow_ys()[0] + self.get_x()
    }

    pub fn set_a(&mut self, a: f64) {
        self.a = a;
        self.clear_cache();
    }
}

fn main() {
    let mut foo = Foo::new(1.0);
    println!("{}", foo.test());
    foo.set_a(3.0);
    println!("{}", foo.test());
}

它打印

Set ys to [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Set x to 1
2
Set ys to [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
Set x to 3
5

Playground

最佳答案

您需要具有清除缓存的能力这一事实意味着您必须具有防护措施。否则,对set_a的调用可能会使borrow_ys先前返回的裸引用无效。编译器可以验证未发生这种情况的唯一方法是返回防护并从防护中借用。

如果可以清除缓存,则可以使用 LazyCell crate 中的lazycell类型代替。

关于rust - 使用 `Cell`和 `RefCell`进行内存或懒惰评估的惯用方式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44574028/

10-10 18:54