1- use std:: cell:: RefCell ;
1+ use std:: {
2+ cell:: RefCell ,
3+ rc:: { Rc , Weak } ,
4+ } ;
25
3- use crate :: { Observable , call_stack , remove_from_cache , store_in_cache, touch} ;
6+ use crate :: { IObservable , memo_stack , store_in_cache, touch} ;
47
58/// A memoized reactive computation that caches its result and tracks dependencies.
69///
@@ -17,31 +20,36 @@ use crate::{Observable, call_stack, remove_from_cache, store_in_cache, touch};
1720/// - `T`: The result type of the computation. Must implement `Clone`.
1821pub struct Memo < T > {
1922 f : Box < dyn Fn ( ) -> T > ,
20- dependents : RefCell < Vec < & ' static dyn Observable > > ,
21- }
22-
23- impl < T > Observable for Memo < T > {
24- fn invalidate ( & ' static self ) {
25- remove_from_cache ( self ) ;
26- self . dependents . borrow ( ) . iter ( ) . for_each ( |d| d. invalidate ( ) ) ;
27- }
23+ dependents : RefCell < Vec < Weak < dyn IMemo > > > ,
24+ /// A self-referential weak pointer, set during construction with `Rc::new_cyclic`.
25+ /// Used to upgrade to `Rc<Memo<T>>` and then coerce into `Rc<dyn IMemo>` when needed.
26+ weak : Weak < Memo < T > > ,
2827}
2928
3029impl < T > Memo < T > {
3130 /// Creates a new `Memo` wrapping the provided closure.
3231 ///
32+ /// # Requirements
33+ /// - `T` must be `'static`, because the value is stored in global cache.
34+ /// - The closure must be `'static` as well.
35+ ///
3336 /// # Examples
3437 ///
3538 /// ```
3639 /// use reactive_cache::Memo;
3740 ///
3841 /// let memo = Memo::new(|| 10);
42+ /// assert_eq!(memo.get(), 10);
3943 /// ```
40- pub fn new ( f : impl Fn ( ) -> T + ' static ) -> Self {
41- Memo {
44+ pub fn new ( f : impl Fn ( ) -> T + ' static ) -> Rc < Self >
45+ where
46+ T : ' static ,
47+ {
48+ Rc :: new_cyclic ( |weak| Memo {
4249 f : Box :: new ( f) ,
4350 dependents : vec ! [ ] . into ( ) ,
44- }
51+ weak : weak. clone ( ) ,
52+ } )
4553 }
4654
4755 /// Returns the memoized value, recomputing it only if necessary.
@@ -51,37 +59,45 @@ impl<T> Memo<T> {
5159 /// # Examples
5260 ///
5361 /// ```
54- /// use once_cell::unsync::Lazy;
5562 /// use reactive_cache::Memo;
5663 ///
57- /// static mut MEMO: Lazy<Memo<i32>> = Lazy::new(|| Memo::new(|| 5) );
58- /// assert_eq!(unsafe { (*MEMO) .get() } , 5);
64+ /// let memo = Memo::new(|| 5);
65+ /// assert_eq!(memo .get(), 5);
5966 /// ```
60- pub fn get ( & ' static self ) -> T
67+ pub fn get ( & self ) -> T
6168 where
62- T : Clone ,
69+ T : Clone + ' static ,
6370 {
64- if let Some ( last) = call_stack:: last ( )
65- && !self
66- . dependents
67- . borrow ( )
68- . iter ( )
69- . any ( |d| std:: ptr:: eq ( * d, * last) )
70- {
71- self . dependents . borrow_mut ( ) . push ( * last) ;
72- }
71+ self . dependency_collection ( ) ;
7372
74- call_stack :: push ( self ) ;
73+ memo_stack :: push ( self . weak . clone ( ) ) ;
7574
76- let rc = if let Some ( rc) = touch ( self ) {
77- rc
75+ let rc = if let Some ( this) = self . weak . upgrade ( ) {
76+ let key: Rc < dyn IMemo > = this. clone ( ) ;
77+ if let Some ( rc) = touch ( & key) {
78+ rc
79+ } else {
80+ let result: T = ( self . f ) ( ) ;
81+ store_in_cache ( & key, result)
82+ }
7883 } else {
79- let result: T = ( self . f ) ( ) ;
80- store_in_cache ( self , result)
84+ unreachable ! ( )
8185 } ;
8286
83- call_stack :: pop ( ) ;
87+ memo_stack :: pop ( ) ;
8488
8589 ( * rc) . clone ( )
8690 }
8791}
92+
93+ impl < T > IObservable for Memo < T > {
94+ fn dependents ( & self ) -> & RefCell < Vec < Weak < dyn IMemo > > > {
95+ & self . dependents
96+ }
97+ }
98+
99+ /// Internal marker trait for all memoized computations.
100+ /// Used for type erasure when storing heterogeneous `Memo<T>` in caches.
101+ pub ( crate ) trait IMemo : IObservable { }
102+
103+ impl < T > IMemo for Memo < T > { }
0 commit comments