There are a number of possible "liftings" and conversions that combinators can allow:
T, vs Result<T, E>, vs Future<Item = T, Error = E>, vs IntoFuture<Item =T, Error = E>. The current combinators are a mix.From/Into.We recently added a combinator specifically for the latter.
In general, though, it'd be nice to make the combinators consistent and maximally flexible -- but it's easy to run into problems with type inference. Let's iterate on this.
Automatic conversion between error types sounds scary to me.
I think we will likely want to have something like
impl <T, E> ::std::ops::Carrier for Box<Future<Item=T,Error=E>> {
type Success = T;
type Error = E;
//...
}
so that we can conveniently use try!() / ? when working with futures. In such cases we will already have an automatic call to e.into(). If on top of that we also decide to add automatic conversion between error types, then we'll frequently get chained calls to .into(), and the typechecker will not be happy.
Another option would be to play around with blanket IntoFuture impls, and we could even enlist specialization depending on the various timelines in play here. I'm also quite interested to see if we can leverage ?, but I'm also somewhat dubious we could get it to work.
I agree with @dwrensha that too many silent conversions could get unwieldy, but in general I think it's fine to inject From::from in tons of places as it's what ? is doing anyway (at least on the error side)
With the current proposal for the ? trait, it's half-way possible to work with something like tokio.
This would be possible:
impl QuestionMark<Async<T>, io::Error> for io::Result<T> {
fn ask(self) -> Poll<T, io::Error> {
match self {
Ok(v) => Ok(Async::Ready(v)),
Err(e) => match e.kind() {
io::ErrorKind::WouldBlock => Ok(Async::NotReady),
_ => Err(e),
}
}
}
}
However, like I said, it only gets us half-way.
let something = self.inner.poll()?;
The above will make something of type Async<T>, so you still need to match and then return the NotReady case.
To get this to work, it seems 2 directions could be taken:
QuestionMark (Carrier/Try) trait to not use Result<T, E>, but something that allows the de-sugaring to return even when Ok(Async::NotReady).NotReady back in to the Err case, similar to how WouldBlock is an error case.Of course, the choice to do neither could be taken, but seems like then we miss out on an excellent language feature, ?.
The ? is in stable now, it would be really nice to be able to use this for futures. I end up temped to wrap my functions in Result<_, Future> so that I can use ?.
https://github.com/rust-lang-nursery/futures-rs/pull/733 has landed on 0.2 which blanket removes From conversions for now, so I'm going to close this
Most helpful comment
The
?is in stable now, it would be really nice to be able to use this for futures. I end up temped to wrap my functions inResult<_, Future>so that I can use?.