Expand description
The typle macro generates code for multiple tuple lengths. This code
use typle::typle;
struct MyStruct<T> {
t: T,
}
#[typle(Tuple for 0..=3)]
impl<T: Tuple> From<T> for MyStruct<T>
{
fn from(t: T) -> Self {
MyStruct { t }
}
}generates implementations of the From trait for tuples with 0 to 3 components:
impl From<()> for MyStruct<()> {
fn from(t: ()) -> Self {
MyStruct { t }
}
}
impl<T0> From<(T0,)> for MyStruct<(T0,)> {
fn from(t: (T0,)) -> Self {
MyStruct { t }
}
}
impl<T0, T1> From<(T0, T1)> for MyStruct<(T0, T1)> {
fn from(t: (T0, T1)) -> Self {
MyStruct { t }
}
}
impl<T0, T1, T2> From<(T0, T1, T2)> for MyStruct<(T0, T1, T2)> {
fn from(t: (T0, T1, T2)) -> Self {
MyStruct { t }
}
}Inside typle code, individual components of a tuple can be selected using
<{i}> for types and [[i]] for values. The value i must be a typle
index expression, an expression that only uses literal usize values or
typle indexes created by one of several macros.
The typle_for! macro creates a new tuple type or value. Inside the macro
the typle index can provide access to each component of an existing tuple
type or value.
// Split off the first component
#[typle(Tuple for 1..=12)]
fn split<T: Tuple>(
t: T // t: (T0, T1, T2,...)
) -> (T<0>, typle_for!(i in 1.. => T<{i}>)) // (T0, (T1, T2,...))
{
(t[[0]], typle_for!(i in 1.. => t[[i]])) // (t.0, (t.1, t.2,...))
}
assert_eq!(split(('1', 2, 3.0)), ('1', (2, 3.0)));
assert_eq!(split((2, 3.0)), (2, (3.0,)));
assert_eq!(split((3.0,)), (3.0, ()));Specify constraints on the tuple components using one of the following
forms. Except for the first form, these constraints can only appear in the
where clause.
T: Tuple<C>- each component of the tuple has typeCT<_>: Copy- each component of the tuple implementsCopyT<0>: Copy- the first component of the tuple implementsCopyT<{1..=2}>: Copy- the second and third components implementCopytyple_bound!- the most general way to bound components, allowing the typle index to be used in the trait bounds, as shown below:
use std::{ops::Mul, time::Duration};
// Multiply the components of two tuples
#[typle(Tuple for 0..=12)]
fn multiply<S: Tuple, T: Tuple>(
s: S, // s: (S0,...)
t: T, // t: (T0,...)
) -> typle_for!(i in .. => <S<{i}> as Mul<T<{i}>>>::Output) // (<S0 as Mul<T0>>::Output,...)
where
typle_bound!(i in .. => S<{i}>): Mul<T<{i}>>, // S0: Mul<T0>,...
{
typle_for!(i in .. => s[[i]] * t[[i]]) // (s.0 * t.0,...)
}
assert_eq!(
multiply((Duration::from_secs(5), 2), (4, 3)),
(Duration::from_secs(20), 6)
)The associated constant LEN provides the length of the tuple in each
generated item. It can be used as a typle index.
Use the typle_index! macro in a for loop to iterate over a range bounded
by typle index expressions.
#[typle(Tuple for 1..=3)]
impl<T, C> MyStruct<T>
where
T: Tuple<C>,
C: for<'a> std::ops::AddAssign<&'a C> + Default,
{
// Return the sums of all odd positions and all even positions.
fn interleave(&self) -> [C; 2] {
let mut odd_even = [C::default(), C::default()];
for typle_index!(i) in 0..T::LEN {
odd_even[i % 2] += &self.t[[i]];
}
odd_even
}
}
let m = MyStruct::from((3, 9, 11));
assert_eq!(m.interleave(), [14, 9]);The next example is simplified from code in the
hefty crate and
demonstrates the use of typle with enums.
The typle_variant! macro creates multiple enum variants by looping
similarly to typle_for!.
Typled enums and structs require a separate identifier for each tuple
length. The typle macro adds the tuple length to their original name. For
example enum TupleSequenceState<T> expands to enum TupleSequenceState3<T0, T1, T2>
for 3-tuples. When referring to these types from other typled items, use
TupleSequenceState<T<{ .. }>>.
Use the typle_ident! macro to concatenate a number to an identifier. For
example S::<typle_ident!(3)> becomes the identifer S3.
The typle_attr_if attribute allows conditional inclusion of attributes. It works similarly to
cfg_attr
except that the first argument is a boolean typle index expression.
The typle_const! macro supports const-if on a boolean typle index
expression. const-if allows branches that do not compile, as long as they
are false at compile-time. For example, this code compiles when
i + 1 == T::LEN even though the identifier S::<typle_ident!(T::LEN)>
(S3 for 3-tuples) is not defined.
use typle::typle;
#[typle(Tuple for 1..=12)]
mod tuple {
pub trait Extract {
type State;
type Output;
fn extract(&self, state: Option<Self::State>) -> Self::Output;
}
pub enum TupleSequenceState<T>
where
T: Tuple,
T<_>: Extract,
{
S = typle_variant!(i in .. =>
typle_for!(j in ..i => T::<{j}>::Output),
Option<T<{i}>::State>
),
}
pub struct TupleSequence<T> {
tuple: T,
}
impl<T> Extract for TupleSequence<T>
where
T: Tuple,
T<_>: Extract,
{
type State = TupleSequenceState<T<{ .. }>>;
type Output = typle_for!(i in .. => T<{i}>::Output);
fn extract(&self, state: Option<Self::State>) -> Self::Output {
#[typle_attr_if(T::LEN == 1, allow(unused_mut))]
let mut state = state.unwrap_or(Self::State::S::<typle_ident!(0)>((), None));
for typle_index!(i) in 0..T::LEN {
// For LEN = 1 there is only one variant (S0) so `let` is irrefutable
#[typle_attr_if(T::LEN == 1, allow(irrefutable_let_patterns, unused_variables))]
if let Self::State::S::<typle_ident!(i)>(output, inner_state) = state {
let matched = self.tuple[[i]].extract(inner_state);
let output = typle_for!(j in ..=i =>
if typle_const!(j != i) { output[[j]] } else { matched }
);
if typle_const!(i + 1 == T::LEN) {
return output;
} else {
state = Self::State::S::<typle_ident!(i + 1)>(output, None);
}
}
}
unreachable!();
}
}
}Generated implementation for 3-tuples:
pub enum TupleSequenceState3<T0, T1, T2>
where
T0: Extract,
T1: Extract,
T2: Extract,
{
S0((), Option<<T0>::State>),
S1((<T0>::Output,), Option<<T1>::State>),
S2((<T0>::Output, <T1>::Output), Option<<T2>::State>),
}
impl<T0, T1, T2> Extract for TupleSequence<(T0, T1, T2)>
where
T0: Extract,
T1: Extract,
T2: Extract,
{
type State = TupleSequenceState3<T0, T1, T2>;
type Output = (<T0>::Output, <T1>::Output, <T2>::Output);
fn extract(&self, state: Option<Self::State>) -> Self::Output {
let mut state = state.unwrap_or(Self::State::S0((), None));
{
{
if let Self::State::S0(output, inner_state) = state {
let matched = self.tuple.0.extract(inner_state);
let output = ({ matched },);
{
state = Self::State::S1(output, None);
}
}
}
{
if let Self::State::S1(output, inner_state) = state {
let matched = self.tuple.1.extract(inner_state);
let output = ({ output.0 }, { matched });
{
state = Self::State::S2(output, None);
}
}
}
{
if let Self::State::S2(output, inner_state) = state {
let matched = self.tuple.2.extract(inner_state);
let output = ({ output.0 }, { output.1 }, { matched });
{
return output;
}
}
}
()
}
unreachable!();
}
}§Limitations
- The typle trait (
Tuplein the examples) cannot be combined with other constraints. To support?Sizedtuples constrain the last component usingT<{T::LEN - 1}>: ?Sized. - Standalone
asyncandunsafefunctions are not supported. - Standalone functions require explicit lifetimes on references
#[typle(Tuple for 1..=3)]
pub fn hash<'a, T, S: Hasher>(tuple: &'a T, state: &'a mut S)
where
T: Tuple,
T<_>: Hash,
T<{T::LEN - 1}>: ?Sized,
{
for typle_index!(i) in 0..T::LEN {
tuple[[i]].hash(state);
}
}- Shadowing of typle indexes is not supported. For example, in:
for typle_index!(i) in 2..=3 {
let i = 1;
func(i)
}func will be called with 2 and 3, never with 1. The same is true for other places where typle
indexes are introduced. For example in a typle_for! macro.
- A
continuereferencing a label on aforloop usingtyple_index!works but displays an unsuppressible warning during compilation.
// warning: label name `'cont` shadows a label name that is already in scope
'cont: for typle_index!(i) in 2..=3 {
loop {
if typle_const!(i == 2) {
continue 'cont;
}
break;
}
}Macros§
- Create a tuple or array.
- Create variants in an enum.