- The example you gave at the bottom won't really work, because enum variants aren't fully-fledged types on their own and they can't impl `Future`. Therefore, you can't `.await` them on their own. Furthermore, the point of `try_join` is to poll both futures until one fails, which you can't do with `async` syntax.
- Rust requires that you spell out all generic parameters like this at function boundaries. It's technically possible to infer types across such boundaries, but that has the potential for significant changes to the typing of a function to go entirely unnoticed. In fact, I believe some of Rust's documentation specifically calls out problems Haskell had with inferring function signatures like this.
That being said, you are correct that we could do this instead:
```pub fn try_join<FutureA, FutureB, ValueA, ValueB, Error>(future_a: FutureA, future_b: FutureB> -> impl Future<Output = Result<(ValueA, ValueB), Error> where FutureA: Future<Output = Result<ValueA, Error>>, FutureB: Future<Output = Result<ValueB, Error>>,```
That is slightly more readable.
At this point, somehow, I'm starting to consider C++ templates beautiful! Perhaps a signature form like:
template <FutureA, FutureB, ValueA, ValueB, Error>
pub fn try_join() ... etc.
would be clearer at this point.
Also, the function only does:
Which is clearer and shorter. Why even writing a function signature like that? It's longer and more complicated to understand than the code itself! I don't know much Rust, but since a semicolon is missing this is just a return value, right?So why not?
PS: I don't want to criticize, and I'm not a Rust literate, I'm just amazed with its complexity.