multi_index_container
A Rust procedural macro for collections that can be efficiently looked up, mutated, and removed by multiple indexes simultaneously, with compile-time guarantees that unique constraints are upheld.
While I have done my best to test it thoroughly, bugs may still be present. If you encounter any issues, bug reports are greatly appreciated.
Overview
multi_index_container lets you define a typed map over any struct where fields can be designated as indexes. Each index can be unique or non-unique, ordered or unordered. The macro generates all lookup, mutation, and removal methods at compile time with no runtime overhead from reflection or dynamic dispatch.
Index Types
| Index type | Duplicates | Ordered |
|---|---|---|
unique |
No | No |
non_unique |
Yes | No |
unique_ordered |
No | Yes |
non_unique_ordered |
Yes | Yes |
Usage
use multi_index_map;
multi_index_map!
This generates a PersonMap type with methods for every index and every combination of indexes.
Documentation
This macro will also generate documentation for the types and methods generated which can be read by generating documenation cargo doc --open --no-deps.
Inserting
let mut map = new;
// Returns Err if a unique constraint is violated
map.insert.unwrap;
// Overwrites any entry that clashes on a unique index; all clashing entries are removed
map.insert_or_overwrite;
// Extend from an iterator; returns a Vec of values that failed to insert
let errors = map.extend;
Looking Up
Single-index lookups are generated for every field:
// Unique indexes return Option<&T>
let alice = map.get_by_email;
// Non-unique indexes return impl Iterator<Item = &T>
let engineers: = map.get_by_department.collect;
// Ordered indexes support range queries
let seniors: = map.get_by_seniority_range.collect;
Combined lookups are generated for every combination of non-unique indexes:
let results: = map
.get_by_department_team
.collect;
let results: = map
.get_by_age_department_team
.collect;
Many index lookups are generated, with the documentation generated running cargo doc --open --no-deps on your crate.
Mutating and Removing
get_mut_by_* returns a MutEntries handle that supports chained operations before modifications:
// Filter, then modify the first match
map.get_mut_by_department_team
.filter
.first
.unwrap
.modify
.unwrap;
// Remove a specific entry
let removed = map
.get_mut_by_email
.unwrap
.remove;
// Remove all matching entries, getting back the evicted values
let removed: = map
.get_mut_by_team
.remove_all;
modify_or_remove
modify_or_remove removes the entry, applies your closure, then re-inserts. If re-insertion fails due to a unique constraint clash, the entry is permanently removed and the failed value is returned in the Err:
let result = map
.get_mut_by_email
.unwrap
.modify_or_remove;
match result
Unique Constraint Violations
Any insertion that would violate a unique or unique_ordered index returns an Err containing the value that could not be inserted. insert_or_overwrite instead removes all clashing entries, including across multiple indexes, and inserts the new value unconditionally.