Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's easy to read, isn't?

pub fn - public function

<...> - declaration of types,

A - type of first future,

B - type of second future,

AR - type of result of first future

BR - type of result of second future

E - type of error

So, public function try_join accepts two futures, which my return <AR>esult and <BR>esult or <E>rror, and returns future which will return tupple with (<AR>, <BR>) or <E>rror.



> It's easy to read, isn't?

    pub fn try_join<A, B, AR, BR, E>(a: A, b: B) -> impl Future<Output = Result<(AR, BR), E>>
    where
        A: Future<Output = Result<AR, E>>,
        B: Future<Output = Result<BR, E>>,
Come on... "A", "B", "E", "a", "b"??? Naming stuff like this is the standard way? I mean, if I know nothing about what the code does, and I find this kind of function, I'll start swearing right away.

Also, the function only does:

    TryJoin::Polling {
        a: State::Future(a),
        b: State::Future(b),
    }
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?

    let res = TryJoin::Polling {
        a: State::Future(fetch_thing("first")),
        b: State::Future(fetch_thing("second")),
    }.await?
PS: I don't want to criticize, and I'm not a Rust literate, I'm just amazed with its complexity.


So, there's a few things to point out here:

- 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.


I'm used to the convention of giving short all-caps acronym names to type parameters, so this is less readable to me, because I automatically assume that something named `Error` is a type, not a type parameter.


Oh god. I guess Rust designers have not foresee a function taking 5 parameters.

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.


To be fair the full expansion of A and B are literally on line below.


> It's easy to read, isn't?

No, it's verbose and ugly. Easy to read would be something like:

  fn try_join(a:A:Future<E||AR>,b:B:Future<E||BR>) -> impl Future<E||(AR,BR)>
(Rounding to the nearest Rust-like syntax where appropriate and borrowing `type (||) = Either` from Haskell.)




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: