4
\$\begingroup\$

I'm pretty new to Rust and to it's async/await model, and I'm trying to do something that looks like a specialized Haskell's traverse function. Given a Vec<T> and a function T -> Future<Output = R> I want to get a Future<Output = Vec<R>>.

At the moment, I have the following:

use futures::{FutureExt, StreamExt};

pub async fn traverse<I, T, R, F, FN>(xs: I, f: FN) -> Vec<R>
where
    I: IntoIterator<Item = T>,
    F: FutureExt<Output = R>,
    FN: Fn(T) -> F,
{   
    futures::stream::iter(xs)
        .fold(vec![], |acc, item| {
            f(item).map(move |app| {
                let mut a = acc;
                a.push(app);

                a
            })
        })
        .await
}

It works as expected, but doesn't feel very idiomatic. Does anybody has suggestion how to improve this function?

\$\endgroup\$
  • \$\begingroup\$ I've edited the title, but I don't see any error in the question. That being said, the above traverse function is a generalized way to transform a Vec of Future to a Future of Vec. you can get back the expected behaviour by passing the vector of Future as first argument and the second argument must be the identity function |x| x \$\endgroup\$ – Molochdaa May 24 at 21:07
5
\$\begingroup\$

I think the best is to simply use then() and collect():

use futures::{Future, StreamExt};

pub async fn traverse<I, T, F, Fut, O>(xs: I, f: F) -> Vec<O>
where
    I: IntoIterator<Item = T>,
    F: Fn(T) -> Fut,
    Fut: Future<Output = O>,
{
    futures::stream::iter(xs).then(f).collect().await
}

Note:

  • futures generic should be name Fut
  • function generic should be name F
  • I rename R as O but I don't know if it's better but it's my way.
  • Doesn't need FutureExt
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.