broomdog

A type-erased map with 'indefinite loanership'

2 releases

0.1.1 Mar 4, 2024
0.1.0 Mar 14, 2023

#553 in Data structures


Used in 3 crates (via moongraph)

MIT/Apache

22KB
395 lines

broomdog ๐Ÿงน๐Ÿ•

broomdog is a Rust library providing a map of type-erased values with indefinite loanership semantics.

what is a type map?

broomdog's type map is a map of std::any::TypeId keys to type-erased values. There may be at most one value for each key, which means the map stores singleton values of different types.

why another type map library?

broomdog provides the usual map-like API, and then some. Notably broomdog provides "indefinite loanership".

wtf is indefinite loanership?

It's a wordsmush that roughly means you can borrow a value from the map without a lifetime.

wtf is a loaned value?

A loaned value is a smart pointer to a value that you can deref or deref_mut.

You may only have one exclusive (write) loan of any one type at a time, but you may have as many non-exclusive (read) loans of the same type as you like. You may also have multiple exclusive loans of different types at the same time.

After an exclusive loan is dropped you may make another exclusive loan of the same type, or multiple non-exclusive loans of that type.

This allows you to make multiple loans of different types at the same time, without map borrow conflicts - so long as you don't try to share an exclusive loan of the same type at the same time.

Furthermore, broomdog has nice descriptive errors with the names of the types (more even, if compiled with debug_assertions).

uses

Type erased maps have many uses, but broomdog was built to facilitate the following:

  • singleton resource storage layer for apecs's system schedule runner (apecs is an ECS library)
  • singleton resource storage layer for renderling's render node schedule runner (part of the render graph)

It works particularly well with dagga, which is a DAG scheduler. In fact, dagga and broomdog are joined in moongraph with some nice helpers to form a DAG scheduling, resource managment and execution library.

Together it's possible to define and run an extensible system of functions that share mutable resources, some of which may run in parallel.

why (the heck) did you name it broomdog

  • "broomdog" is the name of the part broom / part dog character in Alice in Wonderland that erases the path as it walks along it.

  • I think names should be funny.

  • It's my library, oh well ๐Ÿ˜.

example

use broomdog::{TypeMap, TypeKey};

let mut map = TypeMap::default();
assert!(map.insert_value(0usize).unwrap().is_none());
assert!(map.insert_value(0.0f32).unwrap().is_none());
assert!(map.insert_value("hello").unwrap().is_none());

{
    let num_usize = map.get_value_mut::<usize>().unwrap().unwrap();
    *num_usize += 666;
}
assert_eq!(666, *map.get_value::<usize>().unwrap().unwrap());
assert_eq!("hello", *map.get_value::<&str>().unwrap().unwrap());

{
    let loan = map.loan(TypeKey::new::<usize>()).unwrap().unwrap();
    assert_eq!(666, *loan.downcast_ref::<usize>().unwrap());
    let loan2 = map.loan(TypeKey::new::<usize>()).unwrap().unwrap();
    assert_eq!(666, *loan2.downcast_ref::<usize>().unwrap());

    let mut loan_mut = map.loan_mut(TypeKey::new::<&str>()).unwrap().unwrap();
    let word = loan_mut.downcast_mut::<&str>().unwrap();
    assert_eq!("hello", *word);
    *word = "goodbye";
}

map.unify().unwrap();

Dependencies

~0.4โ€“1MB
~21K SLoC