manabreak
manabreak

Reputation: 5597

How to automatically generate incrementing number identifiers for each implementation of a trait?

I have a Component trait that has a method to return an index, like so:

trait Component {
    fn index(&self) -> usize;
}

These indexes are used for setting flags in a bitset. For example, a Component trait object returning an index of 5 would result in 5th bit being set in a container.

Currently I return a running index for each implementing type manually:

struct Foo;
struct Bar;

impl Component for Foo {
    fn index(&self) -> usize { 0 }
}

impl Component for Bar {
    fn index(&self) -> usize { 1 }
}

The trait objects are inserted into a container which keeps track of added components using a bitset:

struct Container<'a> {
    components: Vec<Component + 'a>,
    bits: BitSet
}

impl<'a> Container<'a> {
    fn add<T: Component + 'a>(&mut self, component: T) {
        self.components.push(component);
        self.bits.set(component.index());
    }
}

This works fine, but manually returning the index for each implementing type is cumbersome. How could I make it so that each implementing type would get its index automatically?

Upvotes: 5

Views: 2102

Answers (1)

Peter Hall
Peter Hall

Reputation: 58695

You can define a macro that calls itself recursively:

macro_rules! impl_component {
    // Empty case to end the recursion
    ($n:expr ;) => {};
    // Match the current count, the current type, and whatever else comes after
    ($n:expr ; $t:ty $(, $rest:tt)*) => {
        impl Component for $t {
            fn index(&self) -> usize { $n }
        }
        // Recurse, incrementing counter and only passing the remaining params
        impl_component!($n + 1; $($rest),*);
    };
    // For the first recursion, set the counter initial value to zero
    ($($types:tt),+) => { impl_component!(0; $($types),*); };
}

impl_component!(Foo, Bar, Baz);

The generated code will include implementations like this:

impl Component for Baz {
    fn index(&self) -> usize { 0 + 1 + 1 }
}

The compiler will collapse those expressions into literals, so the result is equivalent to what you wanted.

Upvotes: 7

Related Questions