How to pass tuple as arguments in order to sequentially apply functions with multiple return values in stable Rust?

Viewed 330

I am trying to figure out how to apply two functions in a sequence in Rust, where the first function returns a tuple. So something like:

let f = |x,y| { x+y; };
let g = || (1,2);
println("{}", f(g()));

This won't work. Even if replace g() by a 2-element tuple.

In Julia I could do f(g()...) to expand the tuple into function arguments.

I figured I could define a .apply method to do the expansion for me. The following works, but has to be manually or by macro written for every number of arguments:

trait Apply2<A,B,C>: Fn(A,B) -> C {
    fn apply(&self, _ :(A, B)) -> C;
}

impl<T,A,B,C> Apply2<A,B,C> for T where T: Fn(A,B) -> C {
    fn apply(&self, (a, b) : (A, B)) -> C {
        self(a, b)
    }
}

// and then…

println!("{}", f.apply(g()));

However, the manual/macro-based writing of the traits and implementations is somewhat painful. I guess this should also work:

trait Apply<Args>: Fn<Args> {
    fn apply(&self, _ :Args) -> Self::Output;
}

impl<T,A,B> Apply<(A,B)> for T where T: Fn<(A,B)> {
    fn apply(&self, (a, b) : (A, B)) -> Self::Output {
        self(a, b)
    }
}

However, this is not allowed in "stable" and I'm not going to switch to nightly builds at this point.

Any better ideas? Any plans in Rust to better handle this situation?

1 Answers

You can't do that exactly in stable, but you can cover most of the functions with a macro:

trait Apply<Args> {
    type Output;
    fn apply(&self, args: Args) -> Self::Output;
}

macro_rules! impl_apply {
    // Empty case
    () => {};
    ($first_generic:ident $($other_generics:ident)*) => {
        impl_apply!($($other_generics)*);

        impl<$first_generic, $($other_generics,)* Ret, Func>
            Apply<($first_generic, $($other_generics,)*)>
            for Func
        where
            Func: Fn($first_generic, $($other_generics,)*) -> Ret,
        {
            type Output = Ret;
            #[allow(non_snake_case)]
            fn apply(
                &self,
                ($first_generic, $($other_generics,)*): ($first_generic, $($other_generics,)*),
            ) -> Self::Output {
                self($first_generic, $($other_generics,)*)
            }
        }
    };
}
impl<Ret, Func> Apply<()> for Func
where
    Func: Fn() -> Ret,
{
    type Output = Ret;
    fn apply(&self, (): ()) -> Self::Output {
        self()
    }
}
impl_apply!(A B C D E F G H I J K L M);

Playground.

This macro covers all functions up to 13 arguments.

Related