trait-gen

crate documentation build status crate

This library provides procedural macros to generate the trait implementations for several types, without the need for custom declarative macros. For example,

```rust use traitgen::traitgen;

pub trait IntoU64 { fn into_u64(self) -> u64; }

type T = u64;

[trait_gen(T, i64, u32, i32, u16, i16, u8, i8)]

impl IntoU64 for T { fn into_u64(self) -> u64 { self as u64 } } ```

has the same effect as

```rust pub trait IntoU64 { fn into_u64(self) -> u64; }

macrorules! implintou64 { ($($t:ty)*) => ( $(impl IntoU64 for $t { fn intou64(self) -> u64 { self as u64 } })* ) }

implintou64! { u64 i64 u32 i32 u16 i16 u8 i8 } ```

The advantages of the first method are the clarity of the native code, the support of refactoring tools, code awareness, and not having to convert the code to the declarative macro syntax. Looking for the definition of an implementation method is much easier!

The disadvantage is the lack of support for declarative macros with the IntelliJ plugin, although this is an ongoing work (see tracking issue). There are also a few limitations of the current version described in the Limitations section.

The trait_gen macro

```rust

[trait_gen(type1, type2, type3)]

impl Trait for type1 { // ... } ```

This macro successively substitutes the first type of the list (type1), which is used in the attached source code, with each of the following types (type2, type3) to generate all the variations. So the #[trait_gen(u64, i64, u32, i32)] attribute implements the original source code, then looks for all "u64" types and literally replaces them with i64, u32 and i32 to generate the four implementations.

The code must of course be compatible with all the types, or the compiler will trigger the relevant errors. For example #[trait_gen(u64, f64)] cannot be used with Self(0) because 0 is not a valid floating-point literal.

To avoid any confusion in the attached code between the type to be substituted and possible instances of the same type that must remain in all variations, it is recommended to use an alias type T = <type> and give T as first parameter, like in the first example below. This has the other advantage of improving the clarity. In the same example, using #[trait_gen(u64, i64, u32, ...)] with fn into_u64(self) -> u64 would defeat the purpose of the method by returning i64, u32, ... instead of always returning a u64.

Examples

This example shows how to keep the u64 type in all the implementations by using an alias to indicate what must be substituted. The types are parsed literally, which makes it possible, but this is also why there must not be any other T instance used in a generic, for example (see Limitations for more details).

```rust use traitgen::traitgen;

pub trait ToU64 { fn into_u64(self) -> u64; }

type T = u64;

[trait_gen(T, i64, u32, i32, u16, i16, u8, i8)]

impl ToU64 for T { fn into_u64(self) -> u64 { self as u64 } } ```

When there is little risk of confusion in the example below, because Meter type is unlikely to be used in all the variations. In that case, using an alias is unnecessary.

```rust use std::ops::Add; use traitgen::traitgen;

pub struct Meter(f64); pub struct Foot(f64); pub struct Mile(f64);

[trait_gen(Meter, Foot, Mile)]

impl Add for Meter { type Output = Meter;

fn add(self, rhs: Meter) -> Self::Output {
    Self(self.0 + rhs.0)
}

} ```

Limitations

```rust

[trait_gen(T, Foot, Mile)]

impl Neutral for T { fn mul_neutral(&self) -> Self { T(1.0) // <== ERROR, use Self(1.0) instead } } ```

```rust use num::Num; use traitgen::traitgen;

trait AddMod { type Output; fn add_mod(self, rhs: Self, modulo: Self) -> Self::Output; }

type T = u64;

[trait_gen(T, 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)
}

} ```

Notes

Usage

Add this dependency to your Cargo.toml file:

toml [dependencies] trait-gen = "0"

Compatibility

The trait-gen crate is tested for rustc 1.67.1 and greater, on Windows 64-bit and Linux 64/32-bit platforms. There shouldn't be any problem with older versions.

Releases

RELEASES.md keeps a log of all the releases.

License

Licensed under MIT license.