Crate trait_gen

source ·
Expand description

The trait_gen library

This library provides an attribute macro to generate the trait implementations for several types without needing custom declarative macros, code repetition, or blanket implementations. It makes the code easier to read and to maintain.

Here is a short example:

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

The trait_gen attribute generates the following code by replacing T with the types given as arguments:

impl MyLog for u8 {
    fn my_log2(self) -> u32 {
        u8::BITS - 1 - self.leading_zeros()
    }
}
impl MyLog for u16 {
    fn my_log2(self) -> u32 {
        u16::BITS - 1 - self.leading_zeros()
    }
}
// and so on for the remaining types

Usage

The attribute is placed before the pseudo-generic implementation code. The generic argument is given first, followed by a right arrow (->) and a list of type arguments.

#[trait_gen(T -> Type1, Type2, Type3)]
impl Trait for T {
    // ...
}

The attribute macro successively substitutes the generic argument T in the code with the following types (Type1, Type2, Type3) to generate all the implementations.

All the type paths beginning with T in the code have this part replaced. For example, T::default() generates Type1::default(), Type2::default() and so on, but super::T is unchanged because it belongs to another scope.

The code must be compatible with all the types, or the compiler will trigger the relevant errors. For example #[trait_gen(T -> u64, f64)] cannot be applied to let x: T = 0; because 0 is not a valid floating-point literal.

Finally, the actual type replaces any ${T} occurrence in doc comments, macros, and string literals.

Notes:

  • Using the letter “T” is not mandatory; any type path will do. For example, gen::Type is fine too. But to make it easy to read and similar to a generic implementation, short upper-case identifiers are preferred.
  • Two or more attributes can be chained to generate all the combinations.
  • trait_gen can be used on type implementations too.

For more examples, look at the README.md or the crate integration tests.

Legacy Format

The attribute used a shorter format in earlier versions, which is still supported even though it may be more confusing to read:

#[trait_gen(Type1, Type2, Type3)]
impl Trait for Type1 {
    // ...
}

is a shortcut for the equivalent attribute with the other format:

#[trait_gen(Type1 -> Type1, Type2, Type3)]
impl Trait for Type1 {
    // ...
}

Alternative Format

An alternative format is also supported when the in_format feature is enabled:

trait-gen = { version="0.3", features=["in_format"] }

Warning: This feature is temporary, and there is no guarantee that it will be maintained.

Here, in is used instead of an arrow ->, and the argument types must be between square brackets:

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

Using this format issues ‘deprecated’ warnings that you can turn off by adding the #![allow(deprecated)] directive at the top of the file or by adding #[allow(deprecated)] where the generated code is used.

Limitations

  • The procedural macro of the trait_gen attribute can’t handle scopes, so it doesn’t support any type declaration with the same literal as the generic argument. For instance, this code fails to compile because of the generic function:

    #[trait_gen(T -> u64, i64, u32, i32)]
    impl AddMod for T {
        type Output = T;
    
        fn add_mod(self, rhs: Self, modulo: Self) -> Self::Output {
            fn int_mod<T: Num> (a: T, m: T) -> T { // <== ERROR, conflicting 'T'
                a % m
            }
            int_mod(self + rhs, modulo)
        }
    }
  • The generic argument must be a type path; it cannot be a more complex type like a reference or a slice. So you can use gen::T<U> -> ... but not &T -> ....

Attribute Macros

  • Generates the attached trait implementation for all the types given in argument.