How to return Future Either Or Error

#1

Assume I have following code:

use future::{err, ok};
use futures::Future;
use std::io::Error;
use tokio::prelude::future::Either;
use tokio::prelude::*;

struct Foo {}

fn read_foo_a() -> impl Future<Item = Foo, Error = Error> {
    ok(Foo {})
}

fn read_foo_b() -> impl Future<Item = Foo, Error = Error> {
    ok(Foo {})
}

fn read_foo(arg: u8) -> impl Future<Item = Foo, Error = Error> {
    let fut = if arg == 1 {
        Either::A(read_foo_a())
    } else {
        Either::B(read_foo_b())
    };
    if arg != 3 {
        return fut;
    } else {
        err(Error::from_raw_os_error(0))
    }
}

fn main() {
    println!("Hello, world!");
}

So I need to return from read_foo Foo struct or error and then use something like: try_ready!(read_foo().poll()))
Is there any restrictions to Errors in Futures? Maybe Error has to be () type?

#2

The only restriction (as in the compiler error) is to only being allowed a single structure type. Sometimes Box<dyn ... can be used, in your example the following constructs that single type. (Not entirely sure if you want something different.)

    if arg != 3 {
        ok(())
    } else {
        err(Error::from_raw_os_error(0))
    }.and_then(|()| fut)
#3

I need read_foo function to return Future<Item=Foo, Error=Error>. If I don’t return Error (use panic! for example) from this function everything is ok

#4

When you return Either all returns must use Either. So your error must also be an error that belongs to one of the sides of Either.

Keep in mind that impl Future is not an abstraction. It doesn’t mean any future type. It still returns exactly Either<Something>. With impl Trait you just don’t write the full type in the source code, but the compiler replaces that impl with the actual specific, single type.

#5

So I need something like Either to three types? I’m still not have strong understanding of how futures works. Is there a way to return a Error somehow?

#6

You can nested Eithers within Eithers to get “multi-Eithers”. For example:

fn read_foo(arg: u8) -> impl Future<Item = Foo, Error = Error> {
    let fut = if arg == 1 {
        Either::A(read_foo_a())
    } else {
        Either::B(read_foo_b())
    };
    if arg != 3 {
        return Either::A(fut);
    } else {
        Either::B(err(Error::from_raw_os_error(0)))
    }
}

The futures library supports any kind of Error type you want, but in order to compose them together they usually have to be chosen consistently. If they are not consistent, you can use map_err to transform the Error type however you please.

1 Like
#7

The function is not allowed to return an error directly. The function can only return a future, which can contain either a success or error value. The error you return has to be wrapped in a future of some type, and in this case the function returns Either, so every single error you return has to be wrapped in this Either.

So the only valid ways to return an error from your function are Either::A(future_with_error) or Either::B(future_with_error). return error is a mistake, like trying to return an error from a function that returns String.

If your error type matches error type of one of the either variants, then return error for that variant. If it doesn’t match, then you have to either convert it to one that matches, or make a 3-way Either-like type, or nest Either<Either>, or use Box<dyn Future>.

Either is a kind of optimization that takes advantage of very strict types. If you don’t need to deal with this, you can return Box<dyn Future> which dynamically accepts any future type, with any error type, and you can have literally a thousand different types of return Box::new(future) in your function.