Crate enumap

source ·
Expand description

HashMap and HashSet like interfaces for enumerations backed by an array.

enumap is no_std compatible, dependency and proc macro free for blazingly fast compilation speeds.

use enumap::{EnumMap, EnumSet};

enumap::enumap! {
    /// A beautiful fruit, ready to be sold.
    #[derive(Debug)]
    enum Fruit {
        Orange,
        Banana,
        Grape,
    }
}

// A fruit shop: fruit -> stock.
let mut shop = EnumMap::new();
let mut orders = EnumSet::new();

shop.insert(Fruit::Orange, 100);
shop.insert(Fruit::Banana, 200);

for (fruit, amount) in &shop {
    println!("There are {amount} {fruit:?}s in stock!");
}

if !shop.contains_key(Fruit::Grape) {
    println!("Sorry no grapes in stock :(");
    orders.insert(Fruit::Grape);
}

for fruit in &orders {
    println!("{fruit:?} needs to be ordered!");
}

§Differences and Alternatives

Why would you want to use enumap over one of the alternatives:

  • strum’s EnumTable
  • array_map
  • enum_map

All of these crates expect you to fully initialize the mapping on construction of the map/table. enumap is always backed by an optional storage and therefor can provide a much better API which follows the HashMap API.

enumap does not rely on a proc macro and is dependency free which makes it overall a more lightweight and faster compiling dependency.

These crates do not have support for sets.

§Use Niches

The map is backed by an array of options [Option<V>; N], consider using values with a gurantueed niche to optimize the size of the map:

use enumap::EnumMap;
use std::num::NonZeroUsize;

enumap::enumap! {
    #[derive(Debug)]
    enum Fruit {
        Orange,
        Banana
    }
}

assert_eq!(std::mem::size_of::<EnumMap<2, Fruit, usize>>(), 32);
assert_eq!(std::mem::size_of::<EnumMap<2, Fruit, NonZeroUsize>>(), 16);

§Advanced: Implementing Enum

While the crate was built with enums in mind, it is just a generic map implementation backed by an array which only requires a bijective mapping of items to an array index.

use enumap::{Enum, EnumMap};

#[derive(Copy, Clone)]
struct ZeroToTen(u8);

impl ZeroToTen {
    fn new(num: u8) -> Option<Self> {
        matches!(num, 0..=9).then_some(Self(num))
    }
}

impl Enum<10> for ZeroToTen {
    fn from_index(index: usize) -> Option<Self> {
        index.try_into().ok().and_then(Self::new)
    }

    fn to_index(value: Self) -> usize {
        value.0 as usize
    }
}

let zero = ZeroToTen::new(0).unwrap();
let five = ZeroToTen::new(5).unwrap();
let nine = ZeroToTen::new(9).unwrap();

let mut map = EnumMap::from([
    (zero, "foo"),
    (nine, "bar"),
]);

assert_eq!(map[zero], "foo");
assert_eq!(map[nine], "bar");
assert_eq!(map.get(five), None);

Of course this is also possible for enums with attached data:

use enumap::{Enum, EnumMap};

#[derive(Copy, Clone)]
enum Foo {
    Always,
    Maybe(bool),
}

impl Enum<3> for Foo {
    fn from_index(index: usize) -> Option<Self> {
        match index {
            0 => Some(Self::Always),
            1 => Some(Self::Maybe(true)),
            2 => Some(Self::Maybe(false)),
            _ => None,
        }
    }

    fn to_index(value: Self) -> usize {
        match value {
            Self::Always => 0,
            Self::Maybe(true) => 1,
            Self::Maybe(false) => 2,
        }
    }
}

let mut map = EnumMap::from([
    (Foo::Always, "foo"),
    (Foo::Maybe(true), "bar"),
]);

assert_eq!(map[Foo::Always], "foo");
assert_eq!(map[Foo::Maybe(true)], "bar");
assert_eq!(map.get(Foo::Maybe(false)), None);

Re-exports§

Modules§

  • A map for enumerations backed by an array.
  • A set for enumerations backed by an array.

Macros§

  • Lightweight macro to automatically implement Enum.

Traits§

  • Enum type, usually implemented using the enumap macro.