### Example: Cache::with_options with OptionsBuilder Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Shows how to construct a Cache using with_options, OptionsBuilder, UnitWeighter, DefaultHashBuilder, and DefaultLifecycle. The example sets estimated_items_capacity and weight_capacity to 10000. ```rust use quick_cache::{sync::{Cache, DefaultLifecycle}, OptionsBuilder, UnitWeighter, DefaultHashBuilder}; Cache::<(String, u64), String>::with_options( OptionsBuilder::new() .estimated_items_capacity(10000) .weight_capacity(10000) .build() .unwrap(), UnitWeighter, DefaultHashBuilder::default(), DefaultLifecycle::default(), ); ``` -------------------------------- ### Implementing and Using Weighter Source: https://docs.rs/quick_cache/latest/quick_cache/trait.Weighter.html An example of implementing the Weighter trait for a custom type and using it to initialize a cache. ```rust use quick_cache::{sync::Cache, Weighter}; #[derive(Clone)] struct StringWeighter; impl Weighter for StringWeighter { fn weight(&self, _key: &u64, val: &String) -> u64 { // Be cautious about zero weights! val.len() as u64 } } let cache = Cache::with_weighter(100, 100_000, StringWeighter); cache.insert(1, "1".to_string()); ``` -------------------------------- ### OptionsBuilder::new Source: https://docs.rs/quick_cache/latest/src/quick_cache/options.rs.html Creates a new OptionsBuilder with default settings. This is the starting point for configuring cache options. ```APIDOC ## OptionsBuilder::new ### Description Creates a new `OptionsBuilder` with default settings. This is the starting point for configuring cache options. ### Method `new` ### Signature ```rust pub fn new() -> Self ``` ### Parameters None. ### Returns A new `OptionsBuilder` instance with default values. ### Example ```rust let builder = OptionsBuilder::new(); ``` ``` -------------------------------- ### Asynchronous Value or Guard Test Setup Source: https://docs.rs/quick_cache/latest/src/quick_cache/lib.rs.html Initializes an asynchronous test for value or guard operations. ```rust #[tokio::test(flavor = "multi_thread")] #[cfg_attr(miri, ignore)] async fn test_value_or_guard_async() { use rand::prelude::*; for _i in 0..5000 { dbg!(_i); ``` -------------------------------- ### Initialize Cache with OptionsBuilder Source: https://docs.rs/quick_cache/latest/quick_cache/struct.OptionsBuilder.html Demonstrates how to use OptionsBuilder to configure and build options for a sync Cache instance. ```rust use quick_cache::{sync::{Cache, DefaultLifecycle}, OptionsBuilder, UnitWeighter, DefaultHashBuilder}; Cache::::with_options( OptionsBuilder::new() .estimated_items_capacity(10000) .weight_capacity(10000) .build() .unwrap(), UnitWeighter, DefaultHashBuilder::default(), DefaultLifecycle::default(), ); ``` -------------------------------- ### Retrieve value with get Source: https://docs.rs/quick_cache/latest/src/quick_cache/shard.rs.html Retrieves a value from the shard by key, updating its hotness. ```rust #[inline] pub fn get(&self, hash: u64, key: &Q) -> Option<&Val> where Q: Hash + Equivalent + ?Sized, { self.get_key_value(hash, key).map(|(_k, v)| v) } ``` -------------------------------- ### Initialize Cache with Various Capacities Source: https://docs.rs/quick_cache/latest/src/quick_cache/lib.rs.html Demonstrates initializing both sync and unsync caches with different key-value types and capacity constraints. ```rust #[test] fn test_new() { sync::Cache::<(u64, u64), u64>::new(0); sync::Cache::<(u64, u64), u64>::new(1); sync::Cache::<(u64, u64), u64>::new(2); sync::Cache::<(u64, u64), u64>::new(3); sync::Cache::<(u64, u64), u64>::new(usize::MAX); sync::Cache::::new(0); sync::Cache::::new(1); sync::Cache::::new(2); sync::Cache::::new(3); sync::Cache::::new(usize::MAX); } ``` -------------------------------- ### start_loading Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.PlaceholderGuard.html Creates a new PlaceholderGuard for a cache shard with a shared placeholder value. ```APIDOC ## start_loading ### Description Creates a new PlaceholderGuard for a cache shard with a shared placeholder value. ### Method start_loading ### Signature pub fn start_loading(shard: &'a RwLock>>>, shared: Arc>) -> Self ### Parameters - **shard** (RwLock>) - Required - The cache shard to associate with the placeholder. - **shared** (Arc>) - Required - The shared placeholder value. ### Returns - **Self** - A new PlaceholderGuard instance. ``` -------------------------------- ### Sequential removal with remove_next Source: https://docs.rs/quick_cache/latest/src/quick_cache/shard.rs.html Removes the next available resident entry starting from a given token. ```rust pub fn remove_next(&mut self, continuation: Option) -> Option<(Token, Key, Val)> { let (token, key, _) = self .entries .iter_from(continuation) .filter_map(|(token, i)| match i { Entry::Resident(r) => Some((token, &r.key, &r.value)), Entry::Placeholder(_) | Entry::Ghost(_) => None, }) .next()?; let hash = Self::hash_static(&self.hash_builder, key); self.remove_internal(hash, token) .map(|(k, v)| (token, k, v)) } ``` -------------------------------- ### Async Entry Actions Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Demonstrates various asynchronous entry actions including Retain, Remove, ReplaceWithGuard, and handling Vacant entries. ```rust /// Tests all basic async entry actions in one test #[tokio::test] async fn test_entry_async_actions() { let cache = Cache::new(100); cache.insert(1, 10); cache.insert(2, 20); // Retain let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await; assert!(matches!(result, EntryResult::Retained(10))); assert_eq!(cache.get(&1), Some(10)); // Remove let result = cache .entry_async(&1, |_k, _v| EntryAction::<()>::Remove) .await; assert!(matches!(result, EntryResult::Removed(1, 10))); assert_eq!(cache.get(&1), None); // ReplaceWithGuard let result = cache .entry_async(&2, |_k, _v| EntryAction::<()>::ReplaceWithGuard) .await; match result { EntryResult::Replaced(g, old) => { assert_eq!(old, 20); let _ = g.insert(42); assert_eq!(cache.get(&2), Some(42)); } _ => panic!("expected Replaced"), } // Vacant let result = cache.entry_async(&3, |_k, v| EntryAction::Retain(*v)).await; match result { EntryResult::Vacant(g) => { let _ = g.insert(99); assert_eq!(cache.get(&3), Some(99)); } _ => panic!("expected Vacant"), } } ``` -------------------------------- ### Test try_get Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Tests the non-blocking get operation, including a case where the operation is blocked by write locks. ```rust #[test] fn test_try_get() { let cache = Cache::new(100); cache.insert(1, 10); assert!(cache.try_get(&1).is_ok_and(|v| matches!(v, Some(10)))); assert!(cache.try_get(&2).is_ok_and(|v| v.is_none())); } #[test] fn test_try_get_contended() { let cache = Cache::new(100); cache.insert(1, 10); let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect(); assert!(cache.try_get(&1).is_err()); } ``` -------------------------------- ### Cache::with Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Creates a new cache with full configuration: weight capacity, weighter, hash builder, and lifecycle. ```APIDOC ## Cache::with ### Description Creates a new cache with the specified weight capacity, weighter, hash builder, and lifecycle. This is the most flexible constructor. ### Method `pub fn with(estimated_items_capacity: usize, weight_capacity: u64, weighter: We, hash_builder: B, lifecycle: L) -> Self` ### Parameters - **estimated_items_capacity** (usize) - Required - The estimated number of items the cache is expected to hold. - **weight_capacity** (u64) - Required - The maximum total weight the cache can hold. - **weighter** (We) - Required - The weighter used to compute item weights. - **hash_builder** (B) - Required - The hash builder used for hashing keys. - **lifecycle** (L) - Required - The lifecycle used for cache entry lifecycle events. ### Returns - **Self** - A new `Cache` instance. ### Example ```rust let cache = Cache::with( 10000, 10000, UnitWeighter, DefaultHashBuilder::default(), DefaultLifecycle::default(), ); ``` ``` -------------------------------- ### OptionsBuilder::new Source: https://docs.rs/quick_cache/latest/quick_cache/struct.OptionsBuilder.html Creates a new OptionsBuilder instance. ```APIDOC ## OptionsBuilder::new ### Description Creates a new OptionsBuilder instance. ### Method new ### Signature pub fn new() -> Self ### Parameters None ### Returns Self - A new OptionsBuilder instance. ### Example ```rust use quick_cache::OptionsBuilder; let builder = OptionsBuilder::new(); ``` ``` -------------------------------- ### Using RequestState with _with_lifecycle methods Source: https://docs.rs/quick_cache/latest/quick_cache/trait.Lifecycle.html Shows how to create a RequestState and pass it to multiple insert_with_lifecycle calls to batch eviction work and inspect evicted items before the state is dropped. ```rust let mut lcs = Default::default(); cache.insert_with_lifecycle(k1, v1, &mut lcs); cache.insert_with_lifecycle(k2, v2, &mut lcs); // inspect `lcs` here if desired; evicted items are released when it drops ``` -------------------------------- ### get_or_insert_with Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Gets or inserts an item in the cache with the given key. If the value is not present, the provided closure is called to compute it. ```APIDOC ## get_or_insert_with ### Description Gets or inserts an item in the cache with key `key`. See also `get_value_or_guard` and `get_value_or_guard_async`. ### Method pub fn get_or_insert_with(&self, key: &Q, with: impl FnOnce() -> Result) -> Result where Q: Hash + Equivalent + ToOwned + ?Sized ### Parameters - **key** (&Q) - Required - The key to look up or insert. - **with** (impl FnOnce() -> Result) - Required - A closure that computes the value if not present. ``` -------------------------------- ### get_mut_or_guard Source: https://docs.rs/quick_cache/latest/src/quick_cache/unsync.rs.html Gets an item from the cache with the given key. If the value is not present, returns a guard that can be used to insert the value once computed. ```APIDOC ## get_mut_or_guard ### Description Gets an item from the cache with the given key. If the value is not present, returns a guard that can be used to insert the value once computed. ### Method Rust method ### Signature pub fn get_mut_or_guard<'a, Q>(&'a mut self, key: &Q) -> Result>, Guard<'a, Key, Val, We, B, L>> ### Parameters - **key** (&Q) - Required - The key to look up. ### Return Value - Result>, Guard<'a, Key, Val, We, B, L>> - Ok(Some(ref)) if the value is present, Ok(None) if not admitted, Err(Guard) if not present. ### Notes Leaking the returned RefMut might cause cache weight tracking to be inaccurate. ``` -------------------------------- ### with_options Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Constructs a new cache with the given options, weighter, hash builder, and lifecycle. This is the primary constructor for creating a cache instance. ```APIDOC ## with_options ### Description Constructs a new cache with the specified options, weighter, hash builder, and lifecycle. This is the primary constructor for creating a cache instance. ### Method Constructor ### Parameters - **options** (Options) - Required - Configuration options for the cache, including shard count, estimated item capacity, weight capacity, and allocation policies. - **weighter** (We) - Required - A weighter function that computes the weight of each cached item. - **hash_builder** (B) - Required - A hash builder implementing the BuildHasher trait for hashing keys. - **lifecycle** (L) - Required - A lifecycle handler for cache events. ### Returns - **Self** - A new cache instance. ### Example ```rust let cache = QuickCache::with_options( Options::default(), weighter, hash_builder, DefaultLifecycle::default(), ); ``` ``` -------------------------------- ### get_ref_or_guard Source: https://docs.rs/quick_cache/latest/src/quick_cache/unsync.rs.html Gets an item from the cache with the given key. If the value is not present, returns a guard that can be used to insert the value once computed. ```APIDOC ## get_ref_or_guard ### Description Gets an item from the cache with the given key. If the value is not present, returns a guard that can be used to insert the value once computed. ### Method Rust method ### Signature pub fn get_ref_or_guard(&mut self, key: &Q) -> Result<&Val, Guard<'_, Key, Val, We, B, L>> ### Parameters - **key** (&Q) - Required - The key to look up. ### Return Value - Result<&Val, Guard<'_, Key, Val, We, B, L>> - Ok(&Val) if the value is present, Err(Guard) if not. ``` -------------------------------- ### get_mut_or_insert_with Source: https://docs.rs/quick_cache/latest/src/quick_cache/unsync.rs.html Gets or inserts an item in the cache with the given key. Returns a mutable reference to the inserted value if it was admitted to the cache. ```APIDOC ## get_mut_or_insert_with ### Description Gets or inserts an item in the cache with the given key. If the key is not present, the provided closure is called to compute the value, and the value is inserted if admitted. Returns a mutable reference to the value if it was admitted. ### Method Rust method ### Signature pub fn get_mut_or_insert_with<'a, Q, E>(&'a mut self, key: &Q, with: impl FnOnce() -> Result) -> Result>, E> ### Parameters - **key** (&Q) - Required - The key to look up. - **with** (impl FnOnce() -> Result) - Required - A closure that returns the value to insert if the key is absent. ### Return Value - Result>, E> - Ok(Some(ref)) if the value is present or inserted, Ok(None) if not admitted, or Err(e) if the closure fails. ### Notes See also get_mut_or_guard. ``` -------------------------------- ### get_or_insert_async Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Asynchronously gets or inserts an item in the cache with the given key. If the value is not present, the provided future is awaited to compute it. ```APIDOC ## get_or_insert_async ### Description Gets or inserts an item in the cache with key `key`. ### Method pub async fn get_or_insert_async(&self, key: &Q, with: impl Future>) -> Result where Q: Hash + Equivalent + ToOwned + ?Sized ### Parameters - **key** (&Q) - Required - The key to look up or insert. - **with** (impl Future>) - Required - A future that computes the value if not present. ``` -------------------------------- ### Cache::with Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Creates a new cache with custom weighter, hash builder, and lifecycle. ```APIDOC ## Cache::with ### Description Creates a new cache that can hold up to `weight_capacity` in weight. `estimated_items_capacity` is the estimated number of items the cache is expected to hold, roughly equivalent to `weight_capacity / average item weight`. ### Method with ### Signature pub fn with(estimated_items_capacity: usize, weight_capacity: u64, weighter: We, hash_builder: B, lifecycle: L) -> Self ### Parameters - **estimated_items_capacity** (usize) - Required - The estimated number of items the cache is expected to hold. - **weight_capacity** (u64) - Required - The maximum total weight of cached items. - **weighter** (We) - Required - The weighter used to compute item weights. - **hash_builder** (B) - Required - The hash builder used for hashing keys. - **lifecycle** (L) - Required - The lifecycle used for cache events. ### Returns A new Cache instance. ``` -------------------------------- ### Manage Cache Capacity and Eviction Source: https://docs.rs/quick_cache/latest/src/quick_cache/lib.rs.html Shows how caches with capacity 1 behave during insertion and eviction, and confirms that capacity 0 stores nothing. ```rust #[test] fn test_capacity_one() { // Sync cache with capacity 1 should be able to hold one item let cache = sync::Cache::::new(1); cache.insert(1, 10); assert_eq!(cache.get(&1), Some(10)); // Inserting a second key should evict the first cache.insert(2, 20); assert_eq!(cache.get(&2), Some(20)); assert_eq!(cache.get(&1), None); // Unsync cache with capacity 1 should also work let mut cache = unsync::Cache::::new(1); cache.insert(1, 10); assert_eq!(cache.get(&1), Some(&10)); cache.insert(2, 20); assert_eq!(cache.get(&2), Some(&20)); assert_eq!(cache.get(&1), None); // Capacity 0 should store nothing let cache = sync::Cache::::new(0); cache.insert(1, 10); assert_eq!(cache.get(&1), None); } ``` -------------------------------- ### Asynchronous Get or Insert Test Source: https://docs.rs/quick_cache/latest/src/quick_cache/lib.rs.html Tests concurrent asynchronous insertion into the cache using get_or_insert_async with a barrier to synchronize task execution. ```rust #[tokio::test(flavor = "multi_thread")] #[cfg_attr(miri, ignore)] async fn test_get_or_insert_async() { use rand::prelude::*; for _i in 0..5000 { dbg!(_i); let entered = Arc::new(AtomicUsize::default()); let cache = Arc::new(sync::Cache::<(u64, u64), u64>::new(100)); const TASKS: usize = 100; let wg = Arc::new(tokio::sync::Barrier::new(TASKS)); let solve_at = rand::rng().random_range(0..TASKS); let mut tasks = Vec::new(); for _ in 0..TASKS { let cache = cache.clone(); let wg = wg.clone(); let entered = entered.clone(); let task = tokio::spawn(async move { wg.wait().await; let result = cache .get_or_insert_async(&(1, 1), async { let before = entered.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if before == solve_at { Ok(1) } else { Err(()) } }) .await; assert!(matches!(result, Ok(1) | Err(()))); }); tasks.push(task); } for task in tasks { task.await.unwrap(); } assert_eq!(cache.get(&(1, 1)), Some(1)); assert_eq!( entered.load(std::sync::atomic::Ordering::Relaxed), solve_at + 1 ); } } ``` -------------------------------- ### get_value_or_guard_async Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Asynchronously gets an item from the cache with the given key. If the value is not present, returns a guard that can be used to insert the value once computed. ```APIDOC ## get_value_or_guard_async ### Description Gets an item from the cache with key `key`. If the corresponding value isn't present in the cache, this function returns a guard that can be used to insert the value once it's computed. While the returned guard is alive, other calls with the same key using the `get_value_or_guard` or `get_or_insert` family of functions will wait until the guard is dropped or the value is inserted. ### Method pub async fn get_value_or_guard_async<'a, Q>(&'a self, key: &Q) -> Result> where Q: Hash + Equivalent + ToOwned + ?Sized ### Parameters - **key** (&Q) - Required - The key to look up. ``` -------------------------------- ### Entry State Transition and Eviction Logic Source: https://docs.rs/quick_cache/latest/src/quick_cache/shard.rs.html Handles the transition of cache entries between states (Ghost, Placeholder, Resident) and manages weight updates and lifecycle events during eviction. ```rust 921 return Err((key, value)); 922 } 923 Entry::Ghost(_) => { 924 referenced = 0; 925 enter_state = ResidentState::Hot; 926 } 927 Entry::Placeholder(ph) => { 928 referenced = 1; // Pretend it's a newly inserted Resident 929 enter_state = ph.hot; 930 } 931 } 932 933 let evicted = mem::replace( 934 entry, 935 Entry::Resident(Resident { 936 key, 937 value, 938 state: enter_state, 939 referenced: referenced.into(), 940 #[cfg(feature = "stats")] 941 access_count: Default::default(), 942 }), 943 ); 944 match evicted { 945 Entry::Resident(evicted) => { 946 debug_assert_eq!(evicted.state, enter_state); 947 let evicted_weight = self.weighter.weight(&evicted.key, &evicted.value); 948 let list_head = if enter_state == ResidentState::Hot { 949 self.weight_hot -= evicted_weight; 950 self.weight_hot += weight; 951 &mut self.hot_head 952 } else { 953 self.weight_cold -= evicted_weight; 954 self.weight_cold += weight; 955 &mut self.cold_head 956 }; 957 if evicted_weight == 0 && weight != 0 { 958 *list_head = Some(self.entries.link(idx, *list_head)); 959 } else if evicted_weight != 0 && weight == 0 { 960 *list_head = self.entries.unlink(idx); 961 } 962 match enter_state { 963 ResidentState::Hot => { 964 self.lifecycle.on_evict_hot(lcs, evicted.key, evicted.value) 965 } 966 ResidentState::Cold => { 967 self.lifecycle 968 .on_evict_cold(lcs, evicted.key, evicted.value) 969 } 970 } 971 } 972 Entry::Ghost(_) => { 973 self.weight_hot += weight; 974 self.num_hot += 1; 975 self.num_non_resident -= 1; 976 let next_ghost = self.entries.unlink(idx); 977 if self.ghost_head == Some(idx) { 978 self.ghost_head = next_ghost; 979 } 980 if weight != 0 { 981 self.hot_head = Some(self.entries.link(idx, self.hot_head)); 982 } 983 } 984 Entry::Placeholder(_) => { 985 let list_head = if enter_state == ResidentState::Hot { 986 self.num_hot += 1; 987 self.weight_hot += weight; 988 &mut self.hot_head 989 } else { 990 self.num_cold += 1; 991 self.weight_cold += weight; 992 &mut self.cold_head 993 }; 994 if weight != 0 { 995 *list_head = Some(self.entries.link(idx, *list_head)); 996 } 997 } 998 } 999 1000 while self.weight_hot + self.weight_cold > self.weight_capacity && self.advance_cold(lcs) {} 1001 Ok(()) 1002 } ``` -------------------------------- ### get_or_insert_async Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Asynchronously gets or inserts an item in the cache with the given key. If the value is not present, the provided future is awaited to compute it, and the result is inserted into the cache. ```APIDOC ## get_or_insert_async ### Description Asynchronously gets or inserts an item in the cache with the given key. If the value is not present, the provided future is awaited to compute it, and the result is inserted into the cache. ### Method pub async fn get_or_insert_async(&self, key: &Q, with: impl Future>) -> Result ### Parameters - **key** (&Q) - Required - The cache key to look up or insert. - **with** (impl Future>) - Required - A future that computes the value if it is not present. It returns a Result; if it returns an error, the error is propagated and no value is inserted. ### Return Value - **Result** - The cached value if present, or the newly computed value. If the future returns an error, that error is returned. ``` -------------------------------- ### entry_or_placeholder Method Source: https://docs.rs/quick_cache/latest/src/quick_cache/shard.rs.html Performs operations on an existing or missing key using a callback. It uses a WeightGuard to ensure weight consistency if the callback panics. ```rust pub fn entry_or_placeholder( &mut self, hash: u64, key: &Q, on_occupied: &mut F, ) -> EntryOrPlaceholder where Q: Hash + Equivalent + ToOwned + ?Sized, F: FnMut(&Key, &mut Val) -> EntryAction, { let idx = self.search(hash, key); if let Some(idx) = idx { let shard = self as *mut _; if let Some((Entry::Resident(r), _)) = self.entries.get_mut(idx) { // Call the callback inside a WeightGuard scope: if it panics // after mutating the value, the guard recomputes weight on drop. // SAFETY: key/value point into the Resident entry at idx, alive // for the duration of the callback. let action = { let (key_ptr, val_ptr) = (&r.key as *const Key, &mut r.value as *mut Val); let _guard = WeightGuard:: { idx, old_weight: self.weighter.weight(&r.key, &r.value), shard, }; on_occupied(unsafe { &*key_ptr }, unsafe { &mut *val_ptr }) }; return match action { EntryAction::Retain(t) => { let Some((Entry::Resident(resident), _)) = self.entries.get_mut(idx) else { // SAFETY: we had a mut reference to the Resident under `idx` until the previous line unsafe { unreachable_unchecked() }; }; if *resident.referenced.get_mut() < MAX_F { *resident.referenced.get_mut() += 1; } record_hit_mut!(self, resident); EntryOrPlaceholder::Kept(t) } EntryAction::Remove => { let (key, val) = self.remove_internal(hash, idx).unwrap(); EntryOrPlaceholder::Removed(key, val) } EntryAction::ReplaceWithGuard => { let Some((Entry::Resident(r), _)) = self.entries.get_mut(idx) else { // SAFETY: we had a mut reference to the Resident under `idx` until the previous line ``` -------------------------------- ### memory_used Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Get total memory used by cache data structures. Note that if cache key or value is some type like `Vec`, the memory allocated in the heap will not be counted. ```APIDOC ## memory_used ### Description Get total memory used by cache data structures. It should be noted that if cache key or value is some type like `Vec`, the memory allocated in the heap will not be counted. ### Method memory_used ### Signature pub fn memory_used(&self) -> MemoryUsed ### Returns - **MemoryUsed** - Total memory used by cache data structures. ``` -------------------------------- ### Test Entry Timeout Behavior Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Demonstrates how the cache handles zero and nonzero timeouts when a placeholder exists or a guard is held. ```rust #[test] #[cfg_attr(miri, ignore)] fn test_entry_timeout() { let cache = Cache::new(100); // Zero timeout — immediate Timeout when placeholder exists let guard = match cache.get_value_or_guard(&1, None) { GuardResult::Guard(g) => g, _ => panic!("expected guard"), }; let result = cache.entry(&1, Some(Duration::ZERO), |_k, v| EntryAction::Retain(*v)); assert!(matches!(result, EntryResult::Timeout)); let _ = guard.insert(1); // Nonzero timeout — guard held longer than timeout let cache = Arc::new(Cache::new(100)); let barrier = Arc::new(Barrier::new(2)); let cache2 = cache.clone(); let barrier2 = barrier.clone(); let holder = thread::spawn(move || { let guard = match cache2.get_value_or_guard(&1, None) { GuardResult::Guard(g) => g, _ => panic!("expected guard"), }; barrier2.wait(); std::thread::sleep(Duration::from_millis(200)); let _ = guard.insert(1); }); barrier.wait(); let result = cache.entry(&1, Some(Duration::from_millis(50)), |_k, v| { EntryAction::Retain(*v) }); assert!(matches!(result, EntryResult::Timeout)); holder.join().unwrap(); } ``` -------------------------------- ### entry_async Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Async version of `Self::entry`. Atomically accesses an existing entry, or gets a guard for insertion. If another task is already loading this key, waits asynchronously for the value. ```APIDOC ## entry_async ### Description Async version of `Self::entry`. Atomically accesses an existing entry, or gets a guard for insertion. If another task is already loading this key, waits asynchronously for the value. See `entry` for full documentation. ### Method entry_async ### Signature pub async fn entry_async<'a, Q, T>(&'a self, key: &Q, on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction) -> EntryResult<'a, Key, Val, We, B, L, T> where Q: Hash + Equivalent + ToOwned + ?Sized ### Parameters - **key** (&Q) - Required - The key to access. - **on_occupied** (impl FnOnce(&Key, &mut Val) -> EntryAction) - Required - Callback invoked with a mutable reference to the key and value if the entry exists. Returns an `EntryAction` to decide what to do. ### Notes This is the async version of `entry`. See `entry` for full documentation. ``` -------------------------------- ### Use Custom Weighter Source: https://docs.rs/quick_cache/latest/src/quick_cache/lib.rs.html Demonstrates using a custom weighter to control cache item weight based on value size. ```rust #[test] fn test_custom_cost() { let cache = sync::Cache::with_weighter(100, 100_000, StringWeighter); cache.insert(1, "1".to_string()); cache.insert(54, "54".to_string()); cache.insert(1000, "1000".to_string()); assert_eq!(cache.get(&1000).unwrap(), "1000"); } ``` -------------------------------- ### Cache::entry Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Atomically accesses an existing entry, or gets a guard for insertion. The callback is invoked with the key and mutable value reference to perform in-place mutation. Returns an EntryResult indicating the outcome. ```APIDOC ## Cache::entry ### Description Atomically accesses an existing entry, or gets a guard for insertion. The callback is invoked with the key and mutable value reference to perform in-place mutation. Returns an EntryResult indicating the outcome. ### Method entry ### Signature pub fn entry(&self, key: &Q, timeout: Option, on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction) -> EntryResult<'_, Key, Val, We, B, L, T> ### Parameters - **key** (&Q) - Required - The key to look up. Must implement Hash, Equivalent, and ToOwned. - **timeout** (Option) - Required - Optional timeout for waiting on a placeholder being loaded by another thread. - **on_occupied** (impl FnOnce(&Key, &mut Val) -> EntryAction) - Required - Callback invoked when the entry is occupied, allowing in-place mutation. Must not call back into the cache from the callback to avoid deadlock. ### Return Value Returns an EntryResult<'_, Key, Val, We, B, L, T> which can be one of: - Retained(T) - The entry was retained with the value returned by the callback. - Removed(Key, Val) - The entry was removed. - Replaced(PlaceholderGuard, Val) - The entry was replaced with a placeholder and the old value. - Vacant(PlaceholderGuard) - The entry was vacant and a placeholder guard is provided for insertion. - Timeout - Waiting for a placeholder timed out. ### Panics If the callback panics, weight accounting is automatically corrected, but any partial mutation to the value will remain. ### Examples ```rust use quick_cache::sync::{Cache, EntryAction, EntryResult}; let cache: Cache = Cache::new(5); cache.insert("counter".to_string(), 0); // Mutate in place: increment a counter let result = cache.entry("counter", None, |_k, v| { *v += 1; EntryAction::Retain(*v) }); assert!(matches!(result, EntryResult::Retained(1))); assert_eq!(cache.get("counter"), Some(1)); ``` ``` -------------------------------- ### Cache Entry Mutation and Removal Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Demonstrates how to use the entry API to retain, mutate, or remove cache entries while tracking weight changes. ```rust // Retain without mutation — weight unchanged let result = cache.entry(&1, None, |_k, _v| EntryAction::Retain(())); assert!(matches!(result, EntryResult::Retained(()))); assert_eq!(cache.weight(), 10); // Mutate to longer string — weight increases let result = cache.entry(&1, None, |_k, v| { v.push_str(" world"); EntryAction::Retain(()) }); assert!(matches!(result, EntryResult::Retained(()))); assert_eq!(cache.weight(), 16); // "hello world" (11) + "world" (5) assert_eq!(cache.get(&1).unwrap(), "hello world"); // Mutate to empty string — weight to zero, entry stays let result = cache.entry(&1, None, |_k, v| { v.clear(); EntryAction::Retain(()) }); assert!(matches!(result, EntryResult::Retained(()))); assert_eq!(cache.weight(), 5); // "" (0) + "world" (5) assert_eq!(cache.get(&1).unwrap(), ""); // Remove — weight decremented let result = cache.entry(&2, None, |_k, _v| EntryAction::<()>::Remove); assert!(matches!(result, EntryResult::Removed(2, _))); assert_eq!(cache.weight(), 0); assert_eq!(cache.len(), 1); // ReplaceWithGuard — old weight gone, new weight after insert cache.insert(3, "hello".to_string()); assert_eq!(cache.weight(), 5); let result = cache.entry(&3, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard); match result { EntryResult::Replaced(g, _old) => { assert_eq!(cache.weight(), 0); let _ = g.insert("hello world!!".to_string()); assert_eq!(cache.weight(), 13); } _ => panic!("expected Replaced"), } } ``` -------------------------------- ### get_value_or_guard Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Gets an item from the cache with the given key. If the value is not present, returns a guard that can be used to insert the value once computed. While the guard is alive, other calls with the same key using the get_value_or_guard or get_or_insert family will wait. ```APIDOC ## get_value_or_guard ### Description Gets an item from the cache with key `key`. If the corresponding value isn't present in the cache, this function returns a guard that can be used to insert the value once it's computed. While the returned guard is alive, other calls with the same key using the `get_value_or_guard` or `get_or_insert` family of functions will wait until the guard is dropped or the value is inserted. A `None` `timeout` means waiting forever. A `Some()` timeout will return a Timeout error immediately if the value is not present and a guard is alive elsewhere. ### Method pub fn get_value_or_guard(&self, key: &Q, timeout: Option) -> GuardResult<'_, Key, Val, We, B, L> where Q: Hash + Equivalent + ToOwned + ?Sized ### Parameters - **key** (&Q) - Required - The key to look up. - **timeout** (Option) - Required - Optional timeout duration. `None` means wait forever. `Some()` returns a Timeout error immediately if the value is not present and a guard is alive elsewhere. ``` -------------------------------- ### Cache::with_options Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Constructs a cache based on OptionsBuilder. ```APIDOC ## Cache::with_options ### Description Constructs a cache based on OptionsBuilder. ### Method with_options ### Signature pub fn with_options(options: Options, weighter: We, hash_builder: B, lifecycle: L) -> Self ### Parameters - **options** (Options) - Required - The options for the cache. - **weighter** (We) - Required - The weighter used to compute item weights. - **hash_builder** (B) - Required - The hash builder used for hashing keys. - **lifecycle** (L) - Required - The lifecycle used for cache events. ### Example ```rust use quick_cache::{sync::{Cache, DefaultLifecycle}, OptionsBuilder, UnitWeighter, DefaultHashBuilder}; Cache::<(String, u64), String>::with_options( OptionsBuilder::new() .estimated_items_capacity(10000) .weight_capacity(10000) .build() .unwrap(), UnitWeighter, DefaultHashBuilder::default(), DefaultLifecycle::default(), ); ``` ``` -------------------------------- ### take Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Drain.html Creates an iterator that yields the first n elements, or fewer if the underlying iterator ends sooner. ```APIDOC ## take ### Description Creates an iterator that yields the first n elements, or fewer if the underlying iterator ends sooner. ### Signature fn take(self, n: usize) -> Take where Self: Sized ### Parameters - **n** (usize) - Required - The number of elements to take. ### Return Value Returns a Take iterator adapter. ### Stability Stable since Rust 1.0.0 (const: unstable). ``` -------------------------------- ### entry Source: https://docs.rs/quick_cache/latest/quick_cache/sync/struct.Cache.html Atomically accesses an existing entry, or gets a guard for insertion. If a value exists, the callback is called with a mutable reference to the key and value, and returns an EntryAction to decide what to do. If no value exists, a PlaceholderGuard is returned for inserting a new value. ```APIDOC ## entry ### Description Atomically accesses an existing entry, or gets a guard for insertion. If a value exists for `key`, `on_occupied` is called with a mutable reference to the key and value. The callback returns an `EntryAction` to decide what to do. If no value exists, a `PlaceholderGuard` is returned for inserting a new value. If another thread is already loading this key, waits up to `timeout` for the value to arrive, then calls `on_occupied` on the result. ### Method entry ### Signature pub fn entry(&self, key: &Q, timeout: Option, on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction) -> EntryResult<'_, Key, Val, We, B, L, T> where Q: Hash + Equivalent + ToOwned + ?Sized ### Parameters - **key** (&Q) - Required - The key to access. - **timeout** (Option) - Required - Maximum time to wait for a value if another thread is loading. `None` means wait forever. `Some()` returns a Timeout immediately if a guard is alive elsewhere. - **on_occupied** (impl FnOnce(&Key, &mut Val) -> EntryAction) - Required - Callback invoked with a mutable reference to the key and value if the entry exists. Returns an `EntryAction` to decide what to do. ### Callback Return Values - **EntryAction::Retain(T)** - Keep the entry, return T. Weight is recalculated after the callback returns. - **EntryAction::Remove** - Remove the entry from the cache. - **EntryAction::ReplaceWithGuard** - Remove the entry and get a guard for re-insertion. ### Performance Always acquires a **write lock** on the shard. For read-only lookups where contention matters, prefer `get`, `get_value_or_guard` or similar. The callback runs under the shard write lock — keep it short to avoid blocking other operations on the same shard. **Do not** call back into the cache from the callback, as this will deadlock when the same shard is accessed. ### Panics If the callback panics, weight accounting is automatically corrected. However, any partial mutation to the value will remain. ### Example ```rust use quick_cache::sync::{Cache, EntryAction, EntryResult}; let cache: Cache = Cache::new(5); cache.insert("counter".to_string(), 0); // Mutate in place: increment a counter let result = cache.entry("counter", None, |_k, v| { *v += 1; EntryAction::Retain(*v) }); assert!(matches!(result, EntryResult::Retained(1))); assert_eq!(cache.get("counter"), Some(1)); ``` ``` -------------------------------- ### Test try_insert_with_lifecycle Source: https://docs.rs/quick_cache/latest/src/quick_cache/sync.rs.html Demonstrates using lifecycle tracking with try_insert, including handling contention. ```rust #[test] fn test_try_insert_with_lifecycle() { let cache = Cache::new(100); let mut lcs = Default::default(); // Successful insert records evictions into the provided request state. assert_eq!(cache.try_insert_with_lifecycle(1, 10, &mut lcs), Ok(())); assert_eq!(cache.get(&1), Some(10)); // The same request state can be threaded through several operations. assert_eq!(cache.try_insert_with_lifecycle(2, 20, &mut lcs), Ok(())); assert_eq!(cache.get(&2), Some(20)); // Contended when a read lock is held. let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect(); assert_eq!( cache.try_insert_with_lifecycle(3, 30, &mut lcs), Err((3, 30)) ); drop(guards); assert_eq!(cache.get(&3), None); } ```