How to conditionally run one of two functions with tokio::join?

Viewed 173

I need to run 4 functions concurrently but one of them is different based on user input.

If I use "if-else" I get "if and else have incompatible types" due to Future.

The only way I see is to make a third function that selects from the other two, but it doesn't allow (to my knowledge) to run concurrently due to awaiting.

Another way is to make two different join! but that seems "code expensive".

What should I do in this situation?

tokio::join!(
    self.func1(),
    if self.flag() {
        self.func2()
    } else {
        self.func3()
    },
    self.func4(),
    self.func5()
);

The function signature is as follows:

pub async fn funcN(&self) -> Result<Vec<String>> {
3 Answers

The probably easiest way is to use a Box and dynamic dispatch. For example, this compiles:

tokio::join!(
    func1(),
    if flag() {
        Box::pin(func2()) as Pin<Box<dyn Future<Output = _>>>
    } else {
        Box::pin(func3()) as Pin<Box<dyn Future<Output = _>>>
    },
    func4(),
    func5()
);

Playground

You can use if inside an async block to produce a single future type from two:

tokio::join!(
    func1(),
    async {
        if flag() {
            func2().await
        } else {
            func3().await
        }
    },
    func4(),
    func5()
);

This has slightly different scheduling than the other answers: the flag() call is made part of the future rather than run immediately. If this is undesirable, you could put the boolean result in a variable beforehand.

Other than that, this is much like the Either approach, but can generalize to more than two choices.

You can use the futures::future::Either enum:

tokio::join!(
    self.func1(),
    if self.flag() {
        Either::Left(self.func2())
    } else {
        Either::Right(self.func3())
    },
    self.func4(),
    self.func5()
);

Playground

Related