代码之家  ›  专栏  ›  技术社区  ›  manabreak

如何为每个trait实现自动生成递增的数字标识符?

  •  2
  • manabreak  · 技术社区  · 6 年前

    我有一个 Component 具有返回索引方法的特征,如:

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

    这些索引用于设置位集中的标志。例如,一个 组成部分 trait对象返回索引5将导致在容器中设置第5位。

    当前,我手动返回每个实现类型的运行索引:

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

    trait对象被插入到容器中,该容器使用位集跟踪添加的组件:

    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());
        }
    }
    

    这很好,但是手动返回每个实现类型的索引是很麻烦的。我怎样才能使每个实现类型都能自动获取其索引?

    1 回复  |  直到 6 年前
        1
  •  5
  •   Shepmaster Tim Diekmann    6 年前

    可以定义递归调用自身的宏:

    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);
    

    生成的代码将包括如下实现:

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

    编译器会将这些表达式折叠成文本,因此结果与您所需的结果相等。