2017-06-15 78 views
0

下面的代碼有效。它分別評估xys,並分別緩存到Foo::x: Cell,Foo::ys: RefCell使用`Cell`和`RefCell`進行記憶或懶惰評估的習慣性方式

但是,我覺得可能有更好的方法來做到這一點。我不喜歡我必須做一個包裝CacheVecGuard,這樣在通話現場,我可以使用self.borrow_ys()而不是冗長的&self.ys.borrow().1

我該如何改進這段代碼?

是否有任何規範片段做懶惰評估或memoization適合在這種情況下? (我知道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

回答

2

,你需要清除緩存的能力,這意味着你必須有一個守衛。否則,致電set_a可能會使borrow_ys之前返回的裸露參考無效。編譯器可以驗證這種情況不會發生的唯一方法就是返回一名警衛並從警衛借用。

如果您可以取消清除緩存的功能,則可以使用lazycell箱中的LazyCell類型。