#traits #map #dynamic #downcast

macro trait-map-derive

Derive the TraitMapEntry trait for trait_map crate

3 releases

0.1.2 Feb 17, 2023
0.1.1 Feb 17, 2023
0.1.0 Feb 17, 2023

#24 in #downcast


Used in trait-map

GPL-3.0-or-later

19KB
149 lines

Trait Map

Rust map for dynamic trait storage and references

Note: this library must be compiled on Rust Nightly. It uses the ptr_metadata and unsize features.

About

This crate allows you to dynamically search for traits inside a map. For example, assume we have some traits and structs that implement those traits:

trait ExampleTrait {
  fn do_something(&self) -> u32;
  fn do_another_thing(&mut self);
}

trait ExampleTraitTwo {
  fn test_method(&self);
}

struct MyStruct {
  // ...
}

struct AnotherStruct {
  // ...
}

impl ExampleTrait for MyStruct {
  fn do_something(&self) -> u32 { /* Code */ }
  fn do_another_thing(&mut self) { /* Code */ }
}

impl ExampleTrait for AnotherStruct {
  fn do_something(&self) -> u32 { /* Code */ }
  fn do_another_thing(&mut self) { /* Code */ }
}

impl ExampleTraitTwo for AnotherStruct{
  fn test_method(&self) { /* Code */ }
}

We can use TraitMap to have iterators over the dyn Trait types as follows:

use trait_map::{TraitMap, TraitMapEntry, Context};

impl TraitMapEntry for MyStruct {
  fn on_create<'a>(&mut self, context: Context<'a>) {
    // Must explicitly list which traits to expose
    context
      .downcast::<Self>()
      .add_trait::<dyn ExampleTrait>();
  }

  // Can be overridden to update the exposed traits in the map
  fn on_update<'a>(&mut self, context: Context<'a>) {
    context
      .downcast::<Self>()
      .remove_trait::<dyn ExampleTrait>();
  }
}

impl TraitMapEntry for AnotherStruct {
  fn on_create<'a>(&mut self, context: Context<'a>) {
    // Must explicitly list which traits to expose
    context
      .downcast::<Self>()
      .add_trait::<dyn ExampleTrait>()
      .add_trait::<dyn ExampleTraitTwo>();
  }
}

fn main() {
  let mut map = TraitMap::new();
  map.add_entry(MyStruct { /* ... */ });
  map.add_entry(AnotherStruct { /* ... */ });

  // Can iterate over all types that implement ExampleTrait
  //  Notice that entry is "&dyn mut ExampleTrait"
  for (entry_id, entry) in map.get_entries_mut::<dyn ExampleTrait>() {
    entry.do_another_thing();
  }

  // Can iterate over all types that implement ExampleTraitTwo
  //  Notice that entry is "&dyn ExampleTraitTwo"
  for (entry_id, entry) in map.get_entries::<dyn ExampleTraitTwo>() {
    entry.test_method();
  }
}

Deriving

If you enable the derive feature flag, you can automatically derive TraitMapEntry. You need to use one or more #[trait_map(...)] macros to specify which traits to register with the TraitMap. It uses the proc_macro_diagnostic feature to emit helpful warnings when compiling on nightly.

use trait_map::TraitMapEntry;

// ...

#[derive(Debug, TraitMapEntry)]
#[trait_map(ExampleTrait, ExampleTraitTwo)]
#[trait_map(std::fmt::Debug)]
struct DerivedStruct {
  // ...
}

impl ExampleTrait for DerivedStruct {
  fn do_something(&self) -> u32 { /* Code */ }
  fn do_another_thing(&mut self) { /* Code */ }
}

impl ExampleTraitTwo for DerivedStruct{
  fn test_method(&self) { /* Code */ }
}

Dependencies

~1.5MB
~37K SLoC