rust - 使用 `Cell` 和 `RefCell` 进行内存或延迟评估的惯用方法

标签 rust

下面的代码有效。它延迟计算 xys 并缓存到 Foo::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());
}

打印

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 中的 LazyCell 类型而不是 crate 。

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

相关文章:

rust - 如何在不使用 Box 的情况下从特征方法返回匿名类型?

rust - 引用向量中的元素

docker - 使用 Docker 构建缓存 Rust 依赖项

rust - 如何要求两个特征的关联类型相同?

rust - 是否可以在 Rust 的函数中定义可选参数?

rust - 除了 Rust 迭代器中的最后一项之外,如何重复克隆操作?

rust - 特性 `Borrow<String> is not implemented for ` &str`

rust - 在 `as_ref()` 中使用 `assert_eq!()` 时需要类型注解

rust - 文字整数值在 Rust 中是否具有特定类型?

rust - `cfg` 哪个总是真/假?