使用`Cell`和`RefCell`进行记忆或懒惰评估的惯用方法

时间:2017-06-15 18:00:02

标签: rust

以下代码有效。它会懒惰地评估this.dataService.getAccountInfo(this.serviceRequestDto); this.dataService.serviceRequestDtoSource.subscribe((serviceRequestDtoValue : ServiceRequestDto) => { this.serviceRequestDto = serviceRequestDtoValue; console.log('search.serviceRequestDto.length:'+this.serviceRequestDto.accountDtoList.length); // prints 'search.serviceRequestDto.length:0' console.log('search.serviceRequestDto.accountDtoList name:'+this.serviceRequestDto.accountDtoList[0].name); // throws 'vendor.bundle.js:5764 ERROR TypeError: Cannot read property 'name' of undefined(…)' error }); x,并分别缓存到ysFoo::x: Cell

但是,我觉得可能有更好的方法。我不喜欢制作包装Foo::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());
}

Playground

1 个答案:

答案 0 :(得分:2)

您需要清除缓存的能力意味着您必须拥有一名警卫。否则,对set_a的调用可能会使borrow_ys之前返回的裸引用无效。编译器可以验证不会发生这种情况的唯一方法是返回一个后卫并从守卫中借用。

如果您可以取消清除缓存的功能,则可以使用lazycell包中的LazyCell类型。