Purely Functional Data Structures in Rust
Overview
A collection of persistent (immutable) data structures implemented in Rust. These data structures use structural sharing to efficiently create new versions without modifying the original, making them ideal for functional programming, concurrent systems, and applications requiring data versioning.
Features
- Persistent: All operations return new versions while preserving the original
- Thread-safe: Immutable by design, safe to share between threads
- Efficient: Uses structural sharing to minimize memory usage and copying
- Comprehensive: Includes lists, queues, sets, maps, and trees
Data Structures
Implemented
| Structure | Description | Performance |
|---|---|---|
| List | Singly-linked stack/list | push/pop/top: O(1), len: O(1) |
| Queue | FIFO queue with two lists | enqueue: O(1), dequeue: O(1) amortized |
| Set | Ordered set (AVL tree) | insert/remove/find: O(log n) |
| Map | Ordered key-value map (AVL tree) | insert/remove/find: O(log n) |
| HashSet | Hash-based set (HAMT) | insert/remove/find: O(1) average |
| HashMap | Hash-based key-value map (HAMT) | insert/remove/find: O(1) average |
| Tree | Multi-way tree with paths | Varies by operation |
All structures now support iterators!
Installation
Add this to your Cargo.toml:
[]
= "0.6.0"
Quick Start
use ;
// List - Stack operations
let list = empty
.push
.push
.push;
assert_eq!;
assert_eq!;
// Queue - FIFO operations
let queue = empty
.enqueue
.enqueue;
let = queue.dequeue;
assert_eq!;
// Set - Ordered unique elements
let set = empty
.insert
.insert
.insert;
assert!;
assert_eq!; // Sorted
// Map - Ordered key-value pairs
let map = empty
.insert
.insert;
assert_eq!;
// HashSet - Fast unique elements
let hashset = empty
.insert
.insert;
assert!;
// HashMap - Fast key-value pairs
let hashmap = empty
.insert;
assert_eq!;
API Documentation
Common Patterns
All data structures follow similar patterns:
empty()- Create a new empty structurelen()/is_empty()- Query sizeinsert()/push()/enqueue()- Add elementsremove()/pop()/dequeue()- Remove elementsfind()/exist()/top()- Access elementsto_vec()- Convert to vectoriter()- Get an iterator
List
A persistent stack/list with O(1) push and pop operations.
use List;
let list = empty
.push
.push
.push;
// Access top element
assert_eq!;
// Pop returns new list
let list2 = list.pop;
assert_eq!; // Original unchanged
assert_eq!;
// Iterate
for item in list.iter
// Reverse
let reversed = list.rev;
assert_eq!;
Queue
A persistent FIFO queue with amortized O(1) operations.
use Queue;
let queue = empty
.enqueue
.enqueue
.enqueue;
// Dequeue returns element and new queue
let = queue.dequeue;
assert_eq!;
assert_eq!; // Original unchanged
assert_eq!;
// Iterate in FIFO order
for item in queue.iter
Set
An ordered set implemented as a balanced binary tree.
use Set;
let set = empty
.insert
.insert
.insert
.insert; // Duplicate, no effect
assert_eq!;
assert!;
assert!;
// Elements are kept sorted
assert_eq!;
// Remove element
let set2 = set.remove;
assert!; // Original unchanged
assert!;
Map<K, V>
An ordered map implemented as a balanced binary tree.
use Map;
let map = empty
.insert
.insert
.insert;
// Find returns Option<&V>
assert_eq!;
assert_eq!;
// Check existence
assert!;
// Iterate in sorted order by key
for in map.iter
HashSet
A hash-based set using Hash Array Mapped Trie (HAMT).
use HashSet;
let set = empty
.insert
.insert
.insert;
assert!;
assert_eq!;
// Convert to vector (unordered)
let vec = set.to_vec;
assert_eq!;
HashMap<K, V>
A hash-based map using Hash Array Mapped Trie (HAMT).
use HashMap;
let map = empty
.insert
.insert;
assert_eq!;
assert_eq!;
// Remove key
let map2 = map.remove;
assert!; // Original unchanged
assert!;
Tree (Path)
A persistent multi-way tree with path-based navigation.
use Path;
// Create a tree with root
let tree = new;
// Add children
let child1 = tree.add_node;
let child2 = tree.add_node;
// Navigate the tree
let parent = child1.parent;
assert_eq!;
// Get all children of root
let children = tree.children;
assert_eq!;
// Transform entire subtree
let numbers = new
.add_node.parent
.add_node.parent;
let doubled = numbers.apply_recursive;
// Filter tree
let filtered = tree.filter_recursive;
// Flatten to vector of all nodes
let all_nodes = tree.flatten;
Iterators
All data structures now support iteration:
use ;
// List - iterates from top to bottom
let list = empty.push.push.push;
let vec: = list.iter.collect;
assert_eq!;
// Queue - iterates in FIFO order
let queue = empty.enqueue.enqueue.enqueue;
let vec: = queue.iter.collect;
assert_eq!;
// Set - iterates in sorted order
let set = empty.insert.insert.insert;
let vec: = set.iter.collect;
assert_eq!;
// Map - iterates in sorted order by key
let map = empty.insert.insert;
let vec: = map.iter.collect;
assert_eq!;
Performance Characteristics
| Operation | List | Queue | Set/Map | HashSet/HashMap |
|---|---|---|---|---|
| Insert/Push | O(1) | O(1) | O(log n) | O(1) average |
| Remove/Pop | O(1) | O(1)* | O(log n) | O(1) average |
| Find/Exist | O(n) | O(n) | O(log n) | O(1) average |
| Length | O(1) | O(1) | O(1) | O(1) |
| To Vector | O(n) | O(n) | O(n) | O(n) |
| Iterate | O(n) | O(n) | O(n) | O(n) |
*Queue dequeue is O(1) amortized, O(n) worst case when the front list is empty
Use Cases
Persistent data structures are ideal for:
- Functional Programming: Immutable by default, composable operations
- Concurrent Systems: No locks needed, thread-safe by design
- Undo/Redo: Keep history of changes efficiently
- Backtracking Algorithms: Explore multiple paths without copying
- Database Systems: Multi-version concurrency control (MVCC)
- State Management: Redux-like patterns, event sourcing
Implementation Details
- List: Singly-linked list with size caching
- Queue: Two-list implementation (Okasaki's queue)
- Set/Map: AVL trees with height-based balancing
- HashSet/HashMap: Hash Array Mapped Trie with 4-bit branching
- Tree: Multi-way tree with path-based navigation
All structures use Arc for shared ownership and structural sharing.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Testing
The library aims for comprehensive test coverage:
Generate documentation:
Credits
The Set and Map implementations are inspired by the F# compiler code (FSharp.Core/set.fs), known for high performance.
License
BSD-3-Clause License
Recent Changes
Version 0.6.0
- Added iterators for Queue, Map, and Set
- Fixed HashSet inefficiency in insert operation
- Fixed unreachable code in Queue dequeue
- Improved documentation with examples
Bug Fixes
- Fixed incorrect panic message in List::top()
- Optimized HashSet::insert to avoid unnecessary remove/reinsert
- Changed unreachable panic to unreachable!() in Queue::dequeue