CacheableRepr

Struct CacheableRepr 

Source
pub struct CacheableRepr<T: Debug + 'static, I: Fn(&T) -> bool> { /* private fields */ }
Expand description

Wraps a value and ensures that an invariant is maintained while allowing that value to be mutated. The invariant is checked after every mutation. Additionally, this struct allows for cacheable reads of the value. This is useful when the read function is expensive. By default, the caching is lazy, so after a value is read once that same read function will fetch the cached value unless the value has been mutated.

With the feature eager enabled, the crate::EagerCacheLookup trait is implemented for this struct and can be used to cache values eagerly. Whenever the value is mutated, all eager caches will be updated in parallel.

This struct requires that the value has a 'static lifetime. If you need to store a value with a non-static lifetime consider using Repr.

Implementations§

Source§

impl<T: Debug + 'static, I: Fn(&T) -> bool> CacheableRepr<T, I>

Source

pub const fn new(inner: T, invariant: I) -> Self

Creates a new representation invariant with the given value and invariant function.

use repr_rs::CacheableRepr;
#[derive(Debug)]
struct MinMax { min: i32, max: i32 }
CacheableRepr::new(
  MinMax { min: 1, max: 5 },
  |mm| mm.min < mm.max,
);
Source

pub const fn with_msg( inner: T, invariant: I, violation_message: &'static str, ) -> Self

Creates a new representation invariant with the given value, invariant function, and violation message.

use repr_rs::CacheableRepr;
#[derive(Debug)]
struct MinMax { min: i32, max: i32 }
CacheableRepr::with_msg(
  MinMax { min: 1, max: 5 },
  |mm| mm.min < mm.max,
  "min must be less than max",
);
Source

pub fn read(&self) -> &T

Borrows a read-only view of the value in the representation invariant.

use repr_rs::CacheableRepr;
#[derive(Debug)]
struct MinMax { min: i32, max: i32 }
let repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
let view = repr.read();
assert_eq!(1, view.min);
assert_eq!(5, view.max);
Source

pub fn write(&mut self) -> ReprMutator<'_, T, I>

Borrows a mutable view of the value in the representation invariant.

use repr_rs::CacheableRepr;
#[derive(Debug)]
struct MinMax { min: i32, max: i32 }
let mut repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
{
  let view = repr.read();
  assert_eq!(1, view.min);
  assert_eq!(5, view.max);
}
repr.write().min = 4;
let view = repr.read();
assert_eq!(4, view.min);
assert_eq!(5, view.max);

Rust’s borrowing rules prevent the read-only view being held while a mutation occurs. For example, this won’t compile:

use repr_rs::CacheableRepr;
#[derive(Debug)]
struct MinMax { min: i32, max: i32 }
let mut repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
let view = repr.read();
assert_eq!(1, view.min);
assert_eq!(5, view.max);
// error[E0502]: cannot borrow `repr` as mutable because it is also borrowed as immutable
repr.write().min = 4;
assert_eq!(4, view.min);
assert_eq!(5, view.max);
Source

pub fn into_inner(self) -> T

Consumes the representation invariant and returns the inner value.

use repr_rs::Repr;
#[derive(Debug)]
struct MinMax { min: i32, max: i32 }
let repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
let inner = repr.into_inner();
assert_eq!(1, inner.min);
Source

pub fn lazy<R: Clone + 'static>(&mut self, read_fn: fn(&T) -> R) -> R

Borrows a read-only view of the value in the representation invariant and caches the result of the read function. The cache is keyed by the read function’s address, so in general you should use function references instead of closures. It is a bug to perform any side effects in the read function (i.e. reading from a file).

use std::sync::atomic::{AtomicU32, Ordering};
use repr_rs::CacheableRepr;
#[derive(Debug)]
struct Person { name: String }
let mut repr = CacheableRepr::new(Person { name: "Alice and Bob together at last".into() }, |p| !p.name.is_empty());
static READ_SPY: AtomicU32 = AtomicU32::new(0);
fn expensive_read(p: &Person) -> usize {
  // Just for demonstration purposes.
  // Do not do side effects in your read functions!
  READ_SPY.fetch_add(1, Ordering::Relaxed);
  fib(p.name.len())
}
let fib_of_name_len = repr.lazy(expensive_read);
assert_eq!(832040, fib_of_name_len);
// this does not recompute the fibonacci number, it just gets it from the cache!
let fib_of_name_len2 = repr.lazy(expensive_read);
assert_eq!(832040, fib_of_name_len2);
repr.write().name = "Alice".into();
// this recomputes the fibonacci number because the name has changed
let fib_of_name_len3 = repr.lazy(expensive_read);
assert_eq!(5, fib_of_name_len3);
assert_eq!(2, READ_SPY.load(Ordering::Relaxed));

Trait Implementations§

Source§

impl<T: Debug, I: Fn(&T) -> bool> AsRef<T> for CacheableRepr<T, I>

Source§

fn as_ref(&self) -> &T

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl<T: Debug + Clone, I: Fn(&T) -> bool + Clone> Clone for CacheableRepr<T, I>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug, I: Fn(&T) -> bool> Debug for CacheableRepr<T, I>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Debug + Display, I: Fn(&T) -> bool> Display for CacheableRepr<T, I>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Debug + Clone + Sync + Send + 'static, I: Fn(&T) -> bool> EagerCacheLookup<T, I> for CacheableRepr<T, I>

Source§

async fn eager<R: Clone + Sync + Send + 'static>( &mut self, read_fn: fn(&T) -> R, ) -> R

Borrows a read-only view of the value in the representation invariant and caches the result of the read function. The cache is keyed by the read function’s address, so in general you should use function references instead of closures. It is a bug to perform any side effects in the read function (i.e. reading from a file). This cache is updated eagerly, so whenever the value is mutated, all eager caches will be updated in parallel. See CacheableRepr::lazy for a lazy version of this function.

use std::time::Duration;
use std::sync::atomic::{AtomicU32, Ordering};
use repr_rs::{CacheableRepr, EagerCacheLookup};
#[derive(Debug, Clone)]
struct Person { name: String }
let mut repr = CacheableRepr::new(Person { name: "Alice and Bob together at last".into() }, |p| !p.name.is_empty());
static READ_SPY: AtomicU32 = AtomicU32::new(0);
fn expensive_read(p: &Person) -> usize {
  // Just for demonstration purposes.
  // Do not do side effects in your read functions!
  READ_SPY.fetch_add(1, Ordering::Relaxed);
  fib(p.name.len())
}
let fib_of_name_len = repr.eager(expensive_read).await;
assert_eq!(832040, fib_of_name_len);
// this does not recompute the fibonacci number, it just gets it from the cache!
let fib_of_name_len2 = repr.eager(expensive_read).await;
assert_eq!(832040, fib_of_name_len2);
assert_eq!(1, READ_SPY.load(Ordering::Relaxed));
repr.write().name = "Alice".into();
// if we wait a bit we can see that a new value has been computed
tokio::time::sleep(Duration::from_millis(100)).await;
assert_eq!(2, READ_SPY.load(Ordering::Relaxed));
// Now when we fetch it again, we should see the new value without needing to recompute it
let fib_of_name_len3 = repr.eager(expensive_read).await;
assert_eq!(5, fib_of_name_len3);
assert_eq!(2, READ_SPY.load(Ordering::Relaxed));
Source§

fn unregister<R: Clone + Sync + Send + 'static>( &mut self, read_fn: fn(&T) -> R, ) -> bool

Unregisters an eager cache. Returns true if the cache was found and removed.

Source§

impl<T: Debug + 'static, I: Fn(&T) -> bool> From<CacheableRepr<T, I>> for Repr<T, I>

Source§

fn from(value: CacheableRepr<T, I>) -> Self

Converts to this type from the input type.
Source§

impl<T: Debug + 'static, I: Fn(&T) -> bool> From<Repr<T, I>> for CacheableRepr<T, I>

Source§

fn from(value: Repr<T, I>) -> Self

Converts to this type from the input type.
Source§

impl<T: Debug + Hash, I: Fn(&T) -> bool> Hash for CacheableRepr<T, I>

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T: Debug + PartialEq, I: Fn(&T) -> bool> PartialEq for CacheableRepr<T, I>

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<T: Debug + Eq, I: Fn(&T) -> bool> Eq for CacheableRepr<T, I>

Source§

impl<T: Debug + Send, I: Fn(&T) -> bool + Send> Send for CacheableRepr<T, I>

§Safety

We exclusively own all inner values here (both the repr and the caches), so we can safely implement Send for this type.

Source§

impl<T: Debug + Sync, I: Fn(&T) -> bool + Sync> Sync for CacheableRepr<T, I>

§Safety

This is safe because we can only mutate the inner value through the ReprMutator, which can only be created by borrowing the Repr mutably. The only other potential issue could be if the invariant function was not thread safe, which is why we require it to be Sync.

The inner mutation (i.e. adding new caches or updating caches) either requires a mutable borrow or is guarded behind a lock.

Auto Trait Implementations§

§

impl<T, I> !Freeze for CacheableRepr<T, I>

§

impl<T, I> !RefUnwindSafe for CacheableRepr<T, I>

§

impl<T, I> Unpin for CacheableRepr<T, I>
where I: Unpin, T: Unpin,

§

impl<T, I> !UnwindSafe for CacheableRepr<T, I>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.