#proc-macro #traits #generator

macro trait-gen

Trait implementation generator macro

24 releases (10 stable)

new 2.0.4 May 11, 2025
1.2.0 May 2, 2025
0.3.2 Jun 23, 2023
0.2.0 Mar 21, 2023

#2687 in Rust patterns

Download history 282/week @ 2025-01-25 284/week @ 2025-02-01 312/week @ 2025-02-08 251/week @ 2025-02-15 208/week @ 2025-02-22 709/week @ 2025-03-01 628/week @ 2025-03-08 553/week @ 2025-03-15 761/week @ 2025-03-22 642/week @ 2025-03-29 986/week @ 2025-04-05 909/week @ 2025-04-12 603/week @ 2025-04-19 1112/week @ 2025-04-26 916/week @ 2025-05-03 1602/week @ 2025-05-10

4,342 downloads per month
Used in mokapot

MIT/Apache

76KB
903 lines

crate documentation build status crate



The 'trait-gen' Crate

This crate provides attribute macros that generate the attached implementation for all the types given in argument. It was first intended for trait implementations, hence the crate name, but it can also be used for any generic implementation.

Here is a simple example:

use trait_gen::trait_gen;

trait MyLog { fn my_log2(self) -> u32; }

#[trait_gen(T -> u8, u16, u32, u64, u128)]
impl MyLog for T {
    fn my_log2(self) -> u32 {
        T::BITS - 1 - self.leading_zeros()
    }
}

Compositions

trait_gen also replaces the content of inner attributes, so it's possible to chain them and extend the above example to references and smart pointers for all the T types:

#[trait_gen(T -> u8, u16, u32, u64, u128)]
#[trait_gen(U -> &T, &mut T, Box<T>)]
impl MyLog for U {
    /// Logarithm base 2 for `${U}`
    fn my_log2(self) -> u32 {
        MyLog::my_log2(*self)
    }
}

Tuples, Permutations, and Conditional Generation

A more concise format can be used when several arguments share the type lists (in other words, when we need permutations with repetitions, or tuples):

#[trait_gen(T, U -> u8, u16, u32)]

In the following example, we also show the conditional attribute trait_gen_if, which offers more flexibility in the implementations. The condition has the general format <argument> in <types>, or its negation, !<argument> in <types>. The code is respectively included or skipped when the argument is identical to one of the types.

use trait_gen::{trait_gen, trait_gen_if};

#[derive(Clone, PartialEq, Debug)]
struct Wrapper<T>(T);

#[trait_gen(T, U -> u8, u16, u32)]
// The types T and U must be different to avoid the compilation error
// "conflicting implementation in crate `core`: impl<T> From<T> for T"
#[trait_gen_if(!T in U)]
impl From<Wrapper<U>> for Wrapper<T> {
    /// converts Wrapper<${U}> to Wrapper<${T}>
    fn from(value: Wrapper<U>) -> Self {
        Wrapper(T::try_from(value.0)
            .expect(&format!("overflow when converting {} to ${T}", value.0)))
    }
}

That will give us all the conversions from/to u8, u16, and u32, except from the same type since they're already covered by a blanket implementation in the standard library. trait_gen_if is also very useful for selecting constants or removing methods depending on the implementated type.

Note: Thanks to Daniel Vigovszky for giving me the idea of conditional generation! He first implemented it, although quite differently, in a fork called conditional_trait_gen. I had pondered about some use-cases that would require such a feature in an old post but never got around to implementing it until version 1.1.0.

The effect above can be achieved with the following, shorter format, which generates all the combinations of types for T and U such that T != U (2-permutations):

#[trait_gen(T != U -> u8, u16, u32)]
impl From<Wrapper<U>> for Wrapper<T> { /* ... */ }

Other features

Please read the crate documentation for more details.

Motivation

There are other ways to generate multiple implementations:

  • copy them manually, which is tedious, error-prone, and annoying to maintain
  • use a declarative macro
  • use a blanket implementation

Using a declarative macro would give something like this:

macro_rules! impl_my_log {
    ($($t:ty)*) => (
        $(impl MyLog for $t {
            fn my_log2(self) -> u32 {
                $t::BITS - 1 - self.leading_zeros()
            }
        })*
    )
}

impl_my_log! { u8 u16 u32 u64 u128 }

It does the job, but it's harder to read than native code, and IDEs can't provide contextual help or apply refactoring in the macro code very often. It's also quite annoying and unhelpful to get the following line of code when we're looking for the definition of a method when it has been generated by a declarative macro:

impl_my_log! { u8 u16 u32 u64 u128 }

Using a blanket implementation is very powerful but has other drawbacks:

  • It forbids any other implementation except for types of the same crate that are not already under the blanket implementation, so it only works when the implementation can be written for all bound types, current and future.
  • Finding a trait that corresponds to what we need to write is not always possible. The num crate provides a lot of help for primitives, for instance, but not everything is covered.
  • Doing the implementation for a whole range of types isn't always desirable.
  • Even when the operations and constants are covered by traits, it quickly requires a long list of trait bounds.

I felt that, by default of a way to implement a generic code for a list of types in the language itself, a procedural macro was the next best thing.

Compatibility

The trait-gen crate is tested for Rust versions 1.61.0 and stable on Windows 64-bit and Linux 64/32-bit platforms.

Releases

RELEASES.md keeps a log of all the releases (most are on the GitHub release page, too).

Licence

This code is licenced under either MIT License or Apache License 2.0, at your option.

Dependencies

~255–690KB
~16K SLoC