triple_r/
lib.rs

1//! # triple-r: Recycle, Reuse, Reduce
2//!
3//! [![Crates.io](https://img.shields.io/crates/v/triple-r.svg)](https://crates.io/crates/triple-r)
4//! [![Docs.rs](https://docs.rs/triple-r/badge.svg)](https://docs.rs/triple-r)
5//! [![CI](https://github.com/andyquinterom/triple-r/actions/workflows/ci.yml/badge.svg)](https://github.com/andyquinterom/triple-r/actions/workflows/ci.yml)
6//!
7//! `triple-r` is a high-performance Rust library that provides wrappers around standard library collections to enable the reuse of their memory allocations. By recycling the underlying memory of collections like `HashMap` and `Vec`, `triple-r` helps reduce allocation overhead in performance-critical applications.
8//!
9//! ---
10//!
11//! ## The Problem
12//!
13//! In many applications, especially those processing data in loops (e.g., servers, game engines, data processing pipelines), collections are often created, populated, used, and then discarded. This pattern can lead to frequent memory allocations and deallocations, which can become a significant performance bottleneck.
14//!
15//! ```rust
16//! # use std::collections::HashMap;
17//! # let data_stream: Vec<i32> = vec![];
18//! // A typical pattern that causes repeated allocations.
19//! for item in data_stream {
20//!     let mut map = HashMap::new(); // Allocates on every iteration
21//!     // ... populate and use the map ...
22//!     # map.insert(item, item);
23//! } // map is dropped, and its memory is deallocated.
24//! ```
25//!
26//! ## The Solution: `triple-r`
27//!
28//! `triple-r` provides "reusable" versions of standard collections that solve this problem. It wraps collections like `HashMap` and `Vec` in a container that preserves their allocation when they are no longer needed.
29//!
30//! The core of the library is a RAII guard pattern. You `recycle` a reusable container to get a temporary guard object. You use this guard just like a regular collection. When the guard goes out of scope, it clears the collection but keeps the underlying memory allocation, making it ready for the next cycle.
31//!
32//! ```rust
33//! use triple_r::ReusableHashMap;
34//! # let data_stream: Vec<i32> = vec![];
35//!
36//! // Create the reusable container once.
37//! let mut reusable_map = ReusableHashMap::<i32, i32>::default();
38//!
39//! for item in data_stream {
40//!     // Recycle the allocation. This is fast and avoids a new allocation.
41//!     let mut map_guard = reusable_map.recycle();
42//!     // ... populate and use the guard ...
43//!     # map_guard.insert(item, item);
44//! } // guard is dropped, the map is cleared, but the allocation is kept.
45//! ```
46//!
47//! ## Key Features
48//!
49//! - **Allocation Reuse:** Provides [`ReusableHashMap`], [`ReusableVec`], and [`ReusableString`] to avoid repeated memory allocations.
50//! - **Type Casting:** Safely cast the types of the stored elements between uses. For example, a `ReusableHashMap<&'static str, _>` can be recycled into a guard for a `HashMap<&'a str, _>`.
51//! - **Compile-Time Safety:** The API is designed to prevent common misuses at compile time, such as having multiple mutable references to the same underlying collection.
52//! - **Safety Assured:** The internal use of `unsafe` code is minimal and has been carefully designed and verified with `cargo miri` to ensure it is free of undefined behavior.
53//! - **Drop-In Replacement:** The guard objects implement `Deref` and `DerefMut`, so you can use them just like standard `HashMap`, `Vec`, and `String`.
54//!
55//! ## Usage
56//!
57//! ### ReusableHashMap
58//!
59//! ```rust
60//! use triple_r::ReusableHashMap;
61//!
62//! let mut reusable_map = ReusableHashMap::<String, i32>::default();
63//! let mut last_capacity = 0;
64//!
65//! for i in 0..3 {
66//!     // Obtain a guard to the map.
67//!     let mut map_guard = reusable_map.recycle();
68//!
69//!     // The capacity is preserved from the previous iteration.
70//!     assert_eq!(map_guard.capacity(), last_capacity);
71//!
72//!     map_guard.insert(format!("key-{}", i), i);
73//!     assert_eq!(map_guard.get(&format!("key-{}", i)), Some(&i));
74//!     assert_eq!(map_guard.len(), 1);
75//!
76//!     last_capacity = map_guard.capacity();
77//!     // `map_guard` is dropped here, clearing the map.
78//! }
79//!
80//! // After the loop, the map is empty, but the final capacity is retained.
81//! let mut final_guard = reusable_map.recycle::<String, i32>();
82//! assert!(final_guard.is_empty());
83//! assert_eq!(final_guard.capacity(), last_capacity);
84//! ```
85//!
86//! ### ReusableVec
87//!
88//! ```rust
89//! use triple_r::ReusableVec;
90//!
91//! let mut reusable_vec = ReusableVec::<u8>::default();
92//! let mut last_capacity = 0;
93//!
94//! for i in 0..5 {
95//!     // Recycle the vector's allocation.
96//!     let mut vec_guard = reusable_vec.recycle();
97//!     assert!(vec_guard.is_empty());
98//!     assert_eq!(vec_guard.capacity(), last_capacity);
99//!
100//!     vec_guard.extend(0..i as u8);
101//!     assert_eq!(vec_guard.len(), i);
102//!     last_capacity = vec_guard.capacity();
103//! }
104//! ```
105//!
106//! ### ReusableString
107//!
108//! ```rust
109//! use triple_r::ReusableString;
110//!
111//! let mut reusable_string = ReusableString::default();
112//! let mut last_capacity = 0;
113//!
114//! // Use the string multiple times, preserving its capacity.
115//! for _ in 0..3 {
116//!     let mut string_guard = reusable_string.recycle();
117//!     assert!(string_guard.is_empty());
118//!     assert_eq!(string_guard.capacity(), last_capacity);
119//!
120//!     string_guard.push_str("some new content");
121//!     last_capacity = string_guard.capacity();
122//! }
123//!
124//! // The final capacity is retained for the next use.
125//! let final_guard = reusable_string.recycle();
126//! assert!(final_guard.is_empty());
127//! assert!(final_guard.capacity() > 0);
128//! assert_eq!(final_guard.capacity(), last_capacity);
129//! ```
130//!
131//! ### Reusing with Different Lifetimes
132//!
133//! A powerful feature is the ability to change the lifetime of references within the collection. This is useful when you have a long-lived `ReusableHashMap` but need to use it with short-lived data.
134//!
135//! ```rust
136//! use triple_r::ReusableHashMap;
137//!
138//! // The reusable map can hold static string slices.
139//! let mut reusable_map = ReusableHashMap::<&'static str, i32>::default();
140//!
141//! {
142//!     // But we can use it with a short-lived string.
143//!     let short_lived_key = "hello".to_string();
144//!     let mut map_guard = reusable_map.recycle::<&str, i32>(); // Note the type hint
145//!     map_guard.insert(&short_lived_key, 123);
146//!     assert_eq!(map_guard.get("hello"), Some(&123));
147//! } // `short_lived_key` and `map_guard` are dropped here.
148//!
149//! // The allocation is ready for another use.
150//! let mut map_guard = reusable_map.recycle::<&str, i32>();
151//! assert!(map_guard.is_empty());
152//! ```
153//!
154//! ## Safety
155//!
156//! This library uses `unsafe` code to perform the type transmutation and to work with raw pointers inside the guard. The safety of this implementation is ensured by the following principles:
157//!
158//! 1.  **Exclusive Access:** The `recycle()` method requires a mutable reference (`&mut self`) to the [`ReusableHashMap`], [`ReusableVec`], or [`ReusableString`]. This statically guarantees that only one guard can be active at a time, preventing data races.
159//! 2.  **Lifetime Management:** The returned guard is tied to the lifetime of the `&mut self` borrow, ensuring it cannot outlive the container it references.
160//! 3.  **Miri Verification:** The entire codebase is tested with `cargo miri`, a tool that detects undefined behavior in `unsafe` Rust code. All tests pass under Miri, giving strong confidence in the library's soundness.
161pub mod hashmap;
162pub mod string;
163pub mod vec;
164pub use hashmap::{ReusableHashMap, ReusableHashMapGuard};
165pub use string::{ReusableString, ReusableStringGuard};
166pub use vec::{ReusableVec, ReusableVecGuard};
167
168/// A trait that indicates that a type can be safely cast into another type for the
169/// purpose of reusing a collection's allocation.
170///
171/// # Safety
172///
173/// This trait is unsafe to implement because it allows for type transmutation
174/// through pointer casting. Implementers must guarantee that it is safe to
175/// transmute a container of `Self` (e.g., `Vec<Self>`) into a container of `T`
176/// (e.g., `Vec<T>`).
177///
178/// For this library, this is primarily used to change the lifetimes of references
179/// (e.g., from `&'static str` to `&'a str`), which is safe because the
180/// collection is cleared before it is used with the new type, and the new
181/// lifetimes are constrained by the guard's lifetime.
182///
183/// For types with the same memory layout (e.g., primitive integers), this is
184/// also safe.
185pub unsafe trait ReuseCastInto<T> {}
186
187// This implementation allows reusing a map of references with a shorter lifetime.
188// For example, a `HashMap<&'static str, _>` can be reused as a `HashMap<&'a str, _>`.
189unsafe impl<T: ?Sized> ReuseCastInto<&T> for &T {}
190
191macro_rules! impl_reuse_cast_into_for_primitive {
192    ($($t:ty),*) => {
193        $(
194            unsafe impl ReuseCastInto<$t> for $t {}
195        )*
196    };
197}
198
199unsafe impl<T> ReuseCastInto<Vec<T>> for Vec<T> {}
200
201impl_reuse_cast_into_for_primitive!(
202    // Signed integers
203    i8, i16, i32, i64, i128, isize, // Unsigned integers
204    u8, u16, u32, u64, u128, usize, // Floating point numbers
205    f32, f64, // Other primitives
206    bool, char, String
207);