Procedural macros to generalize inherent and trait implementations over tuples.
When it is a need to implement either a trait or a generalized type for a combination of tuples, Rust requires separate implementations to be provided for each tuple variety manually.
This crate provides a proc-macro fortuples!
to write code templates similar to the quote!
macro.
This macro will expand the provided code template for each tuple variety.
Also, an attribute macro #[auto_impl]
that implements a given trait for tuple combinations in a completely automatic way.
This crate is inspired by the impl_trait_for_tuples
.
impl_trait_for_tuples
```rust
struct Vector
fortuples! { #[tuples::membertype(f32)] #[tuples::minsize(2)] #[tuples::maxsize(3)] #[tuples::tuplename(Coords)] impl Vector<#Coords> { fn length(&self) -> f32 { let coords = &self.0;
(#(#coords * #coords)+*).sqrt()
}
}
} ```
for_tuples!
inside the implementation bodyInstead, the fortuples!
macro follows the quote!
-like syntax without extra tokens.
```rust trait Trait { type Ret; type Arg;
fn test(arg: Self::Arg) -> Self::Ret;
} ```
```rust
impl Trait for Tuple { fortuples!( type Ret = ( #( Tuple::Ret ),* ); ); fortuples!( type Arg = ( #( Tuple::Arg ),* ); );
fn test(arg: Self::Arg) -> Self::Ret {
for_tuples!( ( #( Tuple::test(arg.Tuple) ),* ) )
}
} ```
```rust fortuples! { #[tuples::max_size(5)] // <-- optional, default = 16 impl Trait for #Tuple where #(#Member: Trait),* { type Ret = ( #(#Member::Ret),* ); type Arg = ( #(#Member::Arg),* );
fn test(arg: Self::Arg) -> Self::Ret {
( #(#Member::test(#arg)),* )
}
}
} ```
```rust
trait Notify { fn notify(&self); } ```
```rust
trait Notify { fn notify(&self); } ```
fortuples!
proc-macroHere is commented example of fortuples!
usage.
See the fortuples!
macro documentation to learn about the macro settings (like #[tuples::min_size]
).
```rust trait Trait { type Ret;
type Arg;
type FixedType;
const VALUE: i32;
const LENGTH: usize;
fn test_assoc_fn(arg: Self::Arg) -> Self::Ret;
fn test_self_fn(&self) -> Result<(), ()>;
}
fortuples! {
#[tuples::min_size(1)]
// +----- ^^^^^^^^^^^
// | The fortuples!
macro will generate implementations starting with the empty tuple.
// |
// | Due to the min_size
setting,
// | the implementations will start from the (Member0,)
tuple.
impl Trait for #Tuple
// +----------- ^^^^^
// | a meta-variable that will expand to
// | `(Member0,)`, `(Member0, Member1)`, and so on.
where
#(#Member: Trait<FixedType = i32>),*
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// | A repetition -- the code inside the `#(...),*`
// | will expand as many times as many elements are in the current #Tuple.
// |
// | Inside the i-th code fragment, the #Member meta-variable will be substituted
// | by the i-th member type of the current #Tuple.
{
// The `Ret` type will be a tuple consisting of the `Ret` types
// from the current #Tuple member types
type Ret = (#(#Member::Ret),*);
// The `Arg` type will be a tuple consisting of the `Arg` types
// from the current #Tuple member types
type Arg = (#(#Member::Arg),*);
// The `VALUE` will be a sum of all `VALUE`s of the #Tuple member types.
const VALUE: i32 = #(#Member::VALUE)+*;
// +------------------------------- ^
// | Note that a `+` sign separates the `VALUE`s.
const LENGTH: usize = #len(Tuple);
// +----------------- ^^^^^^^^^^^
// | This expands to the current #Tuple length.
type FixedType = i32;
fn test_assoc_fn(arg: Self::Arg) -> Self::Ret {
( #(#Member::test_assoc_fn(#arg)),* )
// +----------------------- ^^^
// | Any identifier after the `#` sign that is neither
// | #Tuple, #Member, nor #len(Tuple)
// | is interpreted as a tuple variable.
// |
// | So the above code will expand like this:
// | ```
// | (
// | Member0::test_assoc_fn(arg.0),
// | Member1::test_assoc_fn(arg.1),
// | ...
// | MemberN::test_assoc_fn(arg.N),
// | )
// | ```
// | where `N` equals `#len(Tuple)`
}
fn test_self_fn(&self) -> Result<(), ()> {
#(#self.test_self_fn()?;)*
// +-------------------- ^
// | Note that there is no separator here.
Ok(())
}
}
} ```
Show the example without comments
fortuples!
proc-macro (without comments)```rust trait Trait { type Ret;
type Arg;
type FixedType;
const VALUE: i32;
const LENGTH: usize;
fn test_assoc_fn(arg: Self::Arg) -> Self::Ret;
fn test_self_fn(&self) -> Result<(), ()>;
}
fortuples! {
#[tuples::min_size(1)]
impl Trait for #Tuple
where
#(#Member: Trait
type Arg = (#(#Member::Arg),*);
const VALUE: i32 = #(#Member::VALUE)+*;
const LENGTH: usize = #len(Tuple);
type FixedType = i32;
fn test_assoc_fn(arg: Self::Arg) -> Self::Ret {
( #(#Member::test_assoc_fn(#arg)),* )
}
fn test_self_fn(&self) -> Result<(), ()> {
#(#self.test_self_fn()?;)*
Ok(())
}
}
} ```
auto_impl
attributeThere is an option to implement a trait
in a completely automatic way using the auto_impl
attribute.
This attribute will automatically generate implementations of the given trait for tuple combinations.
See the auto_impl
documentation to learn about the
attribute's settings and limitations.
```rust
trait AutoImplTrait { fn test(&self, a: i32, b: &f32); } ```