| Crates.io | deferred-map |
| lib.rs | deferred-map |
| version | 0.3.1 |
| created_at | 2025-11-07 14:01:57.641+00 |
| updated_at | 2025-12-27 09:31:22.612339+00 |
| description | High-performance generational arena using handle-based deferred insertion with O(1) operations |
| homepage | https://github.com/ShaoG-R/deferred-map |
| repository | https://github.com/ShaoG-R/deferred-map |
| max_upload_size | |
| id | 1921625 |
| size | 207,805 |
A high-performance generational arena (slotmap) with handle-based deferred insertion for Rust.
Traditional slot maps require you to have the value ready when allocating space. DeferredMap separates these concerns:
This is particularly useful when:
Add this to your Cargo.toml:
[dependencies]
deferred-map = "0.3"
use deferred_map::DeferredMap;
fn main() {
let mut map = DeferredMap::new();
// Step 1: Allocate a handle (reserves a slot)
let handle = map.allocate_handle();
// Step 2: Get the key before inserting
let key = handle.key();
// Step 3: Insert value using the handle
map.insert(handle, "Hello, World!");
// Access the value
assert_eq!(map.get(key), Some(&"Hello, World!"));
// Remove the value
assert_eq!(map.remove(key), Some("Hello, World!"));
}
use deferred_map::DeferredMap;
let mut map = DeferredMap::new();
// Allocate and insert
let handle = map.allocate_handle();
let key = handle.key();
map.insert(handle, 42);
// Get immutable reference
assert_eq!(map.get(key), Some(&42));
// Get mutable reference
if let Some(value) = map.get_mut(key) {
*value = 100;
}
assert_eq!(map.get(key), Some(&100));
// Check existence
assert!(map.contains_key(key));
// Remove value
assert_eq!(map.remove(key), Some(100));
assert_eq!(map.get(key), None);
use deferred_map::{DeferredMap, DefaultKey};
struct Node {
value: i32,
next: Option<DefaultKey>, // Key to next node
}
let mut graph = DeferredMap::new();
// Allocate handles first
let handle1 = graph.allocate_handle();
let handle2 = graph.allocate_handle();
// Get the keys before inserting
let key1 = handle1.key();
let key2 = handle2.key();
// Now we can create nodes that reference each other
let node1 = Node { value: 1, next: Some(key2) };
let node2 = Node { value: 2, next: Some(key1) };
// Insert the nodes
graph.insert(handle1, node1);
graph.insert(handle2, node2);
use deferred_map::DeferredMap;
let mut map = DeferredMap::new();
for i in 0..5 {
let handle = map.allocate_handle();
map.insert(handle, i * 10);
}
// Iterate over all entries
for (key, value) in map.iter() {
println!("Key: {:?}, Value: {}", key, value);
}
// Mutable iteration
for (_, value) in map.iter_mut() {
*value *= 2;
}
use deferred_map::DeferredMap;
let mut map = DeferredMap::<String>::new();
// Allocate a handle
let handle = map.allocate_handle();
// Decide not to use it
map.release_handle(handle);
// The slot is returned to the free list
SecondaryMap allows you to associate additional data with keys from a DeferredMap without modifying the original map or key structure.
use deferred_map::{DeferredMap, SecondaryMap};
let mut map = DeferredMap::new();
let mut sec = SecondaryMap::new();
let h1 = map.allocate_handle();
let k1 = h1.key();
map.insert(h1, "Player 1");
// Associate extra data
sec.insert(k1, 100); // Health points
assert_eq!(sec.get(k1), Some(&100));
// If the key is removed from the main map and reused, SecondaryMap handles it safe
map.remove(k1);
// ... later ...
let h2 = map.allocate_handle(); // Reuses slot
let k2 = h2.key();
map.insert(h2, "Player 2");
// k1 is invalid in sec
assert_eq!(sec.get(k1), None);
// k2 is valid (but empty until inserted)
assert_eq!(sec.get(k2), None);
sec.insert(k2, 200);
assert_eq!(sec.get(k2), Some(&200));
You can find more complete examples in the examples/ directory.
To run the examples:
cargo run --example basic
cargo run --example secondary
DeferredMap<T>: The main map structureHandle: A one-time token for deferred insertion (cannot be cloned)DeferredMap::new() -> Self
DeferredMap::with_capacity(capacity: usize) -> Self
allocate_handle(&mut self) -> Handle
insert(&mut self, handle: Handle, value: T)
release_handle(&mut self, handle: Handle)
handle.key() -> K // Get the key (before insertion)
handle.index() -> u32 // Get the index part
handle.generation() -> u32 // Get the generation part
get(&self, key: K) -> Option<&T>
get_mut(&mut self, key: K) -> Option<&mut T>
remove(&mut self, key: K) -> Option<T>
contains_key(&self, key: K) -> bool
len(&self) -> usize
is_empty(&self) -> bool
capacity(&self) -> usize
clear(&mut self)
iter(&self) -> impl Iterator<Item = (K, &T)>
iter_mut(&mut self) -> impl Iterator<Item = (K, &mut T)>
Each slot in the map can be in one of three states:
Keys are 64-bit values encoding:
This prevents the ABA problem: if you remove a value and reuse the slot, the generation increments, invalidating old keys.
Slots use a union for memory efficiency:
union SlotUnion<T> {
value: ManuallyDrop<T>, // When occupied
next_free: u32, // When vacant (free list pointer)
}
Drop implementation for occupied slots| Feature | deferred-map | slotmap | slab |
|---|---|---|---|
| Deferred Insertion | β | β | β |
| Generational Indices | β | β | β |
| O(1) Operations | β | β | β |
| Handle-based API | β | β | β |
| Memory Efficient | β | β | β |
Rust 1.75 or later (edition 2024)
Licensed under either of:
at your option.
Contributions are welcome! Please feel free to submit a Pull Request.
ShaoG shaog.rs@gmail.com