# EnumMap for Rust
[![Rust](https://github.com/Pscheidl/enum-map/actions/workflows/rust.yml/badge.svg)](https://github.com/Pscheidl/enum-map/actions/workflows/rust.yml)
[![Crates.io](https://img.shields.io/crates/v/enum-collections)](https://crates.io/crates/enum-collections)
[![docs.rs](https://img.shields.io/docsrs/enum-collections)](https://docs.rs/enum-collections/latest/enum_collections/)
[Contribution guide](CONTRIBUTING.md) | [Apache v2 license](LICENSE)
A map of enum variants to values. EnumMap is a fixed-size map, where each variant of the enum is mapped to a value. This implementation of EnumMap uses **safe Rust** only and is a a zero-cost abstraction over an array (**const-sized**), where the index of the array corresponds to the position of the variant in the enum.
Because it is a thin wrapper over an array, it is stack-allocated by default. Simply `std::boxed::Box`ing it will move it to the heap, at the caller's discretion.
- Indexed by enum variants.
- IndexMut by enum variants.
- Debug if the enum is Debug.
- PartialEq if the value is PartialEq. Same for Eq.
Debug and Eq are optional features. They are enabled by default.
## Usage
Please refer to the [documentation](https://docs.rs/enum-collections/latest/enum_collections/) for a complete list of features and more in-depth documentation.
```rust
use enum_collections::{EnumMap, Enumerated, em, em_default, em_option};
#[derive(Enumerated)]
pub enum Letter {
A,
B,
}
// Indexing and mutation
// The SIZE hint is required until [generic_const_expr](https://github.com/rust-lang/rust/issues/76560) are stabilized.
let mut enum_map = EnumMap::::new_default();
assert_eq!(0, enum_map[Letter::A]);
enum_map[Letter::A] = 42;
assert_eq!(42, enum_map[Letter::A]);
// Construction using macros
// (Key type, Value type, Key=>Value pairs)
let enum_map = em!(Letter, i32, A=>42, B=>24); // All values set explicitly
assert_eq!(42, enum_map[Letter::A]);
assert_eq!(24, enum_map[Letter::B]);
// (Key type, Value type, optional Key=>Value pairs)
let enum_map = em_default!(Letter, i32, A => 42); // Default used for missing values
assert_eq!(42, enum_map[Letter::A]);
assert_eq!(i32::default(), enum_map[Letter::B]);
let enum_map = em_default!(Letter, i32,); // All default
assert_eq!(i32::default(), enum_map[Letter::A]);
assert_eq!(i32::default(), enum_map[Letter::B]);
// EnumMap of optional values `Option`, value type is automatically wrapped in `Option`.
// (Key type, Value type, optional Key=>Value pairs)
let enum_map = em_option!(Letter, i32, A => 42);
assert_eq!(Some(42), enum_map[Letter::A]);
assert_eq!(None, enum_map[Letter::B]);
// Constructor with default values
let enum_map_default = EnumMap::::new_default();
assert_eq!(0, enum_map_default[Letter::A]);
assert_eq!(0, enum_map_default[Letter::B]);
// Convenience constructor for optional values
let mut enum_map_option = EnumMap::, { Letter::SIZE }>::new_option();
assert_eq!(None, enum_map_option[Letter::A]);
assert_eq!(None, enum_map_option[Letter::B]);
enum_map_option[Letter::A] = Some(42);
assert_eq!(Some(42), enum_map_option[Letter::A]);
// Constructor with custom initialization
#[derive(PartialEq, Eq, Debug)]
struct Custom;
let enum_map = EnumMap::::new(|| Custom);
assert_eq!(Custom, enum_map[Letter::A]);
assert_eq!(Custom, enum_map[Letter::B]);
// Custom initialization function with enum variant (key) inspection
let enum_map = EnumMap::::new_inspect(|letter| {
match letter {
Letter::A => 42,
Letter::B => 24,
}
});
assert_eq!(42, enum_map[Letter::A]);
assert_eq!(24, enum_map[Letter::B]);
// Debug
#[derive(Enumerated, Debug)]
pub enum LetterDebugDerived {
A,
B,
}
let enum_map_debug =
EnumMap::::new(|| 42);
assert_eq!("{A: 42, B: 42}", format!("{:?}", enum_map_debug));
```
Iterate over enum variants
```rust
#[derive(Enumerated, Debug)]
pub enum Letter {
A,
B,
}
Letter::VARIANTS
.iter()
.for_each(|letter| println!("{:?}", letter));
```
## Features
Portions of functionality are feature-flagged, but enabled by default. This is to allow turning this functionality off when not needed, e.g. `Debug` and `Eq` implementations.
See [docs.rs](https://docs.rs/crate/enum-collections/latest/features) for details.
## Benchmarks
Invoke `cargo bench` to run benchmarks. While `EnumMap` operates in pico-seconds, `std::collections::HashMap` in > 10 nanoseconds.
Benchmark results
```
EnumMap get time: [221.09 ps 221.59 ps 222.21 ps]
Found 10 outliers among 100 measurements (10.00%)
5 (5.00%) high mild
5 (5.00%) high severe
EnumMap insert time: [230.05 ps 233.38 ps 236.25 ps]
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
EnumMap new: default time: [852.31 ps 853.28 ps 854.37 ps]
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) low mild
1 (1.00%) high mild
EnumMap new: Option::None
time: [1.7100 ns 1.7110 ns 1.7120 ns]
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
EnumMap new: provider fn
time: [791.17 ps 792.38 ps 793.65 ps]
Found 7 outliers among 100 measurements (7.00%)
1 (1.00%) low mild
4 (4.00%) high mild
2 (2.00%) high severe
EnumMap new: inspecting provider fn
time: [775.03 ps 776.84 ps 778.92 ps]
Found 8 outliers among 100 measurements (8.00%)
4 (4.00%) high mild
4 (4.00%) high severe
std::collections::HashMap get
time: [13.433 ns 13.484 ns 13.543 ns]
Found 8 outliers among 100 measurements (8.00%)
3 (3.00%) high mild
5 (5.00%) high severe
std::collections::HashMap insert
time: [14.094 ns 14.107 ns 14.121 ns]
Found 4 outliers among 100 measurements (4.00%)
1 (1.00%) high mild
3 (3.00%) high severe
```