//! This example demonstrates how to append an `i32` value to a cached `Vec` //! value. It uses the `and_upsert_with` method of `Cache`. use std::sync::{Arc, RwLock}; use moka::{sync::Cache, Entry}; fn main() { // We want to store a raw value `Vec` for each `String` key. We are going to // append `i32` values to the `Vec` in the cache. // // Note that we have to wrap the `Vec` in an `Arc>`. We need the `Arc`, // an atomic reference counted shared pointer, because `and_upsert_with` method // of `Cache` passes a _clone_ of the value to our closure, instead of passing a // `&mut` reference. We do not want to clone the `Vec` every time we append a // value to it, so we wrap it in an `Arc`. Then we need the `RwLock` because we // mutate the `Vec` when we append a value to it. // // The reason that `and_upsert_with` cannot pass a `&mut Vec<_>` to the closure // is because the internal concurrent hash table of `Cache` is a lock free data // structure and does not use any mutexes. So it cannot guarantee: (1) the `&mut // Vec<_>` is unique, and (2) it is not accessed concurrently by other threads. let cache: Cache>>> = Cache::new(100); let key = "key".to_string(); let entry = append_to_cached_vec(&cache, &key, 1); assert!(entry.is_fresh()); assert!(!entry.is_old_value_replaced()); assert_eq!(*entry.into_value().read().unwrap(), &[1]); let entry = append_to_cached_vec(&cache, &key, 2); assert!(entry.is_fresh()); assert!(entry.is_old_value_replaced()); assert_eq!(*entry.into_value().read().unwrap(), &[1, 2]); let entry = append_to_cached_vec(&cache, &key, 3); assert!(entry.is_fresh()); assert!(entry.is_old_value_replaced()); assert_eq!(*entry.into_value().read().unwrap(), &[1, 2, 3]); } fn append_to_cached_vec( cache: &Cache>>>, key: &str, value: i32, ) -> Entry>>> { cache.entry_by_ref(key).and_upsert_with(|maybe_entry| { if let Some(entry) = maybe_entry { // The entry exists, append the value to the Vec. let v = entry.into_value(); v.write().unwrap().push(value); v } else { // The entry does not exist, insert a new Vec containing // the value. Arc::new(RwLock::new(vec![value])) } }) }