Skip to content

jongiddy/typle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

typle

The typle crate provides the ability to constrain generic arguments to be tuples and supports manipulation of the tuple components.

It allows you to write a single function or impl that "unrolls" into multiple concrete versions: one for (T0,), one for (T0, T1), and so on.

With typle you can easily define a function to zip a pair of tuples into a tuple of pairs:

#[typle(Tuple for 0..=12)]
pub fn zip<A: Tuple, B: Tuple>(a: A, b: B) -> (typle! {
    i in .. => (A<{i}>, B<{i}>)
}) {
    (typle! {
        i in .. => (a[[i]], b[[i]])
    })
}

The #[typle] attribute macro introduces Tuple which acts like a trait within the annotated item. The types A and B are generic but are constrained by the Tuple bound to be tuples. The tuples can have 0 to 12 components of any (sized) type, but both tuples must have the same length.

A<{i}> refers to the type of the tuple component at index i. a[[i]] refers to the value of the tuple component at index i (unrolls to a.0, a.1,...).

The typle! macro loops over a range returning a new sequence with the specified components. For the function return type it creates a type tuple: ((A<0>, B<0>), (A<1>, B<1>),...). In the function body it creates a value tuple: ((a.0, b.0), (a.1, b.1),...).

assert_eq!(
    zip(("LHR", "FCO", "ZRH"), (51.5, 41.8, 47.5)),
    (("LHR", 51.5), ("FCO", 41.8), ("ZRH", 47.5))
);
assert_eq!(
    zip((2.0, "test"), (Some(9u8), ('a', 'b'))),
    ((2.0, Some(9u8)), ("test", ('a', 'b')))
);
assert_eq!(
    zip((), ()),
    ()
);

A common use of typle is to implement a trait for tuples of multiple lengths. Compared to using declarative macros, the typle code looks more Rust-like and provides access to individual components and their position.

trait HandleStuff<I> {
    type Output;

    fn handle_stuff(&self, input: I) -> Self::Output;
}

struct MultipleHandlers<T> {
    handlers: T,
}

#[typle(Tuple for 1..=3)]
impl<I, T> HandleStuff<I> for MultipleHandlers<T>
where
    T: Tuple,             // `T` is a tuple with 1 to 3 components.
    T<_>: HandleStuff<I>, // All components implement `HandleStuff`.
    // Conditionally add `Clone` bound to `I`:
    typle!(=> if T::LEN > 1 { I: Clone }): Tuple::Bounds,
{
    // Return a tuple of the output from each handler.
    type Output = (typle! {i in .. => T<{i}>::Output});

    fn handle_stuff(&self, input: I) -> Self::Output {
        (
            typle! {
                i in ..T::LAST =>
                    self.handlers[[i]].handle_stuff(input.clone())
            },
            // Avoid expensive clone for the last handler.
            self.handlers[[T::LAST]].handle_stuff(input),
        )
    }
}

This generates the implementations

impl<I, T0> HandleStuff<I> for MultipleHandlers<(T0,)>
where
    T0: HandleStuff<I>,
{
    type Output = (T0::Output,);
    fn handle_stuff(&self, input: I) -> Self::Output {
        (self.handlers.0.handle_stuff(input),)
    }
}
impl<I, T0, T1> HandleStuff<I> for MultipleHandlers<(T0, T1)>
where
    T0: HandleStuff<I>,
    T1: HandleStuff<I>,
    I: Clone,
{
    type Output = (T0::Output, T1::Output);
    fn handle_stuff(&self, input: I) -> Self::Output {
        (
            self.handlers.0.handle_stuff(input.clone()),
            self.handlers.1.handle_stuff(input),
        )
    }
}
impl<I, T0, T1, T2> HandleStuff<I> for MultipleHandlers<(T0, T1, T2)>
where
    T0: HandleStuff<I>,
    T1: HandleStuff<I>,
    T2: HandleStuff<I>,
    I: Clone,
{
    type Output = (T0::Output, T1::Output, T2::Output);
    fn handle_stuff(&self, input: I) -> Self::Output {
        (
            self.handlers.0.handle_stuff(input.clone()),
            self.handlers.1.handle_stuff(input.clone()),
            self.handlers.2.handle_stuff(input),
        )
    }
}

See the crate documentation for more examples.

About

Rust macro to create items for different sized tuples

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages