deferred-map
A high-performance generational arena (slotmap) with handle-based deferred insertion for Rust.
Features
- π O(1) Operations: Constant-time insertion, lookup, and removal
- π Memory Safety: Generational indices prevent use-after-free bugs
- β³ Deferred Insertion: Separate handle allocation from value insertion
- πΎ Memory Efficient: Union-based slot storage optimizes memory usage
- π― Type Safe: Handles cannot be cloned, ensuring single-use semantics
- β‘ Zero-Copy: Direct access to stored values without copying
Why Deferred Insertion?
Traditional slot maps require you to have the value ready when allocating space. DeferredMap separates these concerns:
- Allocate Handle - Reserve a slot and get a handle (cheap, no value needed)
- Insert Value - Later, use the handle to insert the actual value
This is particularly useful when:
- Building complex data structures with circular references
- Need to know the key before constructing the value
- Want to reserve capacity before expensive computation
- Coordinating multi-step initialization processes
Installation
Add this to your Cargo.toml:
[]
= "0.3"
Quick Start
use DeferredMap;
Usage Examples
Basic Operations
use DeferredMap;
let mut map = new;
// Allocate and insert
let handle = map.allocate_handle;
let key = handle.key;
map.insert;
// Get immutable reference
assert_eq!;
// Get mutable reference
if let Some = map.get_mut
assert_eq!;
// Check existence
assert!;
// Remove value
assert_eq!;
assert_eq!;
Building Self-Referential Structures
use ;
let mut graph = 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 ;
let node2 = Node ;
// Insert the nodes
graph.insert;
graph.insert;
Iteration
use DeferredMap;
let mut map = new;
for i in 0..5
// Iterate over all entries
for in map.iter
// Mutable iteration
for in map.iter_mut
Releasing Unused Handles
use DeferredMap;
let mut map = new;
// Allocate a handle
let handle = map.allocate_handle;
// Decide not to use it
map.release_handle;
// The slot is returned to the free list
Secondary Map
SecondaryMap allows you to associate additional data with keys from a DeferredMap without modifying the original map or key structure.
use ;
let mut map = new;
let mut sec = new;
let h1 = map.allocate_handle;
let k1 = h1.key;
map.insert;
// Associate extra data
sec.insert; // Health points
assert_eq!;
// If the key is removed from the main map and reused, SecondaryMap handles it safe
map.remove;
// ... later ...
let h2 = map.allocate_handle; // Reuses slot
let k2 = h2.key;
map.insert;
// k1 is invalid in sec
assert_eq!;
// k2 is valid (but empty until inserted)
assert_eq!;
sec.insert;
assert_eq!;
You can find more complete examples in the examples/ directory.
To run the examples:
API Overview
Core Types
DeferredMap<T>: The main map structureHandle: A one-time token for deferred insertion (cannot be cloned)
Main Methods
Creating a Map
new
Handle Operations
allocate_handle
Handle Methods
handle.key // Get the key (before insertion)
handle.index // Get the index part
handle.generation // Get the generation part
Value Access
get
Metadata & Iteration
len
How It Works
Three-State Slot System
Each slot in the map can be in one of three states:
- Vacant (0b00): Empty slot, part of the free list
- Reserved (0b01): Handle allocated, awaiting value insertion
- Occupied (0b11): Contains a valid value
Generational Indices
Keys are 64-bit values encoding:
- Lower 32 bits: Slot index
- Upper 32 bits: Version (including state bits)
This prevents the ABA problem: if you remove a value and reuse the slot, the generation increments, invalidating old keys.
Memory Layout
Slots use a union for memory efficiency:
union
Performance Characteristics
- Insertion: O(1) - Pop from free list
- Lookup: O(1) - Direct array indexing with generation check
- Removal: O(1) - Push to free list
- Memory: Slots are reused, minimal allocation overhead
- Cache Friendly: Contiguous memory layout
Safety Guarantees
- No Use-After-Free: Generational indices ensure old keys are rejected
- No Double-Use: Handles are consumed on use (move semantics)
- No Leaks: Proper
Dropimplementation for occupied slots - Debug Safety: In debug mode, handles are verified to belong to the correct map instance
- Memory Safe: All unsafe code is carefully encapsulated and documented
Comparison with Other Crates
| Feature | deferred-map | slotmap | slab |
|---|---|---|---|
| Deferred Insertion | β | β | β |
| Generational Indices | β | β | β |
| O(1) Operations | β | β | β |
| Handle-based API | β | β | β |
| Memory Efficient | β | β | β |
Minimum Supported Rust Version (MSRV)
Rust 1.75 or later (edition 2024)
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Author
ShaoG shaog.rs@gmail.com