typedmap 0.1.1

A hash map (dash map). A type of value is defined by trait implemented on key type
Documentation

TypedMap

Docs Crates.io

TypedMap is a typed HashMap. It allows you to define different value type depending on a Key type. It's useful if you want to store different key-value pairs in a single hashmap, for example in HTTP app that implements multiple services.

use typedmap::{TypedMap, TypedMapKey};

// Define key types
#[derive(Debug, PartialEq, Eq, Hash)]
struct Dog{name: String};
#[derive(Debug, PartialEq, Eq, Hash)]
struct Cat{name: String};

struct Bark{volume: u32};
struct Mew{pitch: f32};

// Define value type for key types
impl TypedMapKey for Dog {
    type Value = Bark;
}
impl TypedMapKey for Cat {
    type Value = Mew;
}
// Create a new empty map
let mut animal_sounds: TypedMap = TypedMap::new();
// Insert data
animal_sounds.insert(Dog { name: "Spiky".into() }, Bark { volume: 80 });
animal_sounds.insert(Cat { name: "Spot".into()  }, Mew  { pitch: 12000.0 });

// Get for Dog key get value of type Bark.
let spiky_volume = animal_sounds.get(&Dog { name: "Spiky".into() }).unwrap().volume;
assert_eq!(spiky_volume, 80); 
// Define key types
#[derive(Debug, PartialEq, Eq, Hash)]
struct EndpointGet(String);
#[derive(Debug, PartialEq, Eq, Hash)]
struct EndpointPost(usize);

// Define value type for key types
impl TypedMapKey for EndpointGet {
    type Value = String;
}

impl TypedMapKey for EndpointPost {
    type Value = u32;
}

// Create a new empty map
let mut data: TypedMap = TypedMap::new();

// Insert data
data.insert(EndpointGet("test".to_owned()), "data".to_owned());
data.insert(EndpointPost(32), 32);

You can use special Marker type to create more "type" key-value bindings.

struct Configs;
struct Services;

use typedmap::{TypedMap, TypedMapKey};

#[derive(Debug, PartialEq, Eq, Hash)]
struct EndpointGet(String);
#[derive(Debug, PartialEq, Eq, Hash)]
struct EndpointPost(usize);

struct EndpointGetConfig(String);
struct EndpointPostConfig(usize);

struct EndpointGetService {}

impl TypedMapKey<Configs> for EndpointGet {
    type Value = EndpointGetConfig;
}

impl TypedMapKey<Configs> for EndpointPost {
    type Value = EndpointPostConfig;
}

impl TypedMapKey<Services> for EndpointGet {
    type Value = EndpointGetService;
}

impl TypedMapKey<Services> for EndpointPost {
    type Value = EndpointPostService;
}


let mut configs: TypedMap<Configs> = TypedMap::new();
let mut services: TypedMap<Services> = TypedMap::new();

configs.insert(EndpointGet("test".to_owned()), EndpointGetConfig("config".to_owned()));
configs.insert(EndpointPost(10), EndpointPostConfig(20));

services.insert(EndpointGet("test".to_owned()), EndpointGetService {});
services.insert(EndpointPost(10), EndpointPostService {});

If dashmap feature is enabled, one can use TypedDashMap that can be used concurrently, as it's using Dashmap under the hood.

use std::sync::Arc;
use typedmap::TypedDashMap;
use typedmap::TypedMapKey;

struct Configs;
struct Services;

#[derive(Hash, PartialEq, Eq)]
struct ServiceA(usize);

// Implement key-value mapping for Configs marker
impl TypedMapKey<Configs> for ServiceA {
    type Value = usize;
}

// Implement key-value mapping for Services marker
impl TypedMapKey<Services> for ServiceA {
    type Value = &'static str;
}

#[derive(Hash, PartialEq, Eq)]
struct ServiceB(&'static str);

// Implement key-value mapping for Configs marker
impl TypedMapKey<Configs> for ServiceB {
    type Value = Vec<&'static str>;
}

// Implement key-value mapping for Services marker
impl TypedMapKey<Services> for ServiceB {
    type Value = usize;
}

// Implement key-value mapping for default (i.e. ()) marker
impl TypedMapKey for ServiceB {
    type Value = String;
}

let configs: Arc<TypedDashMap<Configs>> = Arc::new(TypedDashMap::new());
let services: Arc<TypedDashMap<Services>> = Arc::new(TypedDashMap::new());
let default: Arc<TypedDashMap> = Arc::new(TypedDashMap::new());

let configs1 = Arc::clone(&configs);
let services1 = Arc::clone(&services);
let t1 = std::thread::spawn(move ||{
    configs1.insert(ServiceA(0), 1);
    services1.insert(ServiceA(0), "one");
});

// Line below would not compile, because TypeMapKey<Marker=()>
// is not implemented for Key.
// default.insert(Key(0), 1);

// Line below would not compile, because SerivceA key defines
// type value as usize for Configs marker (not &'static str)
// configs.insert(ServiceA(0), "one");

let configs2 = Arc::clone(&configs);
let services2 = Arc::clone(&services);
let default2 = Arc::clone(&default);
let t2 = std::thread::spawn(move || {
    configs2.insert(ServiceB("zero"), vec!["one"]);
    services2.insert(ServiceB("zero"), 32);
    default2.insert(ServiceB("zero"), "one".to_owned());
});

t1.join().unwrap();
t2.join().unwrap();

Related:

  • type-map - hashmap that uses types as keys

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.