News for hackers. Stuff that matters.
Re:Understanding Rust futures by going way too deep by MrBuddyCasino on Monday July 26, 2021 @04:31AM (#27957073)
Gaze upon our work, and rejoice:

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

I get why its like that, but I really wished this wasn't a typical Rust generics signature.
Re:Understanding Rust futures by going way too deep by drran on Monday July 26, 2021 @06:46AM (#27957897)
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.

Re:Understanding Rust futures by going way too deep by scoutt on Monday July 26, 2021 @08:41AM (#27958663)
> 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.
Re:Understanding Rust futures by going way too deep by kmeisthax on Monday July 26, 2021 @10:40AM (#27959935)
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.

Re:Understanding Rust futures by going way too deep by greenshackle2 on Monday July 26, 2021 @11:25AM (#27960587)
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.
Re:Understanding Rust futures by going way too deep by scoutt on Monday July 26, 2021 @11:12AM (#27960406)
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.
Re:Understanding Rust futures by going way too deep by gpderetta on Monday July 26, 2021 @10:16AM (#27959608)
To be fair the full expansion of A and B are literally on line below.
Re:Understanding Rust futures by going way too deep by a1369209993 on Monday July 26, 2021 @08:34PM (#27967266)
> 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.)
Re:Understanding Rust futures by going way too deep by paavohtl on Monday July 26, 2021 @04:43AM (#27957134)
I don't think this is a _typical_ signature. On the contrary, it's quite exceptional. In my experience having more than 1 type parameter in a function is quite rare in most code, unless you're building an advanced, composable library like a futures implementation.
Re:Understanding Rust futures by going way too deep by MrBuddyCasino on Monday July 26, 2021 @05:08AM (#27957280)
I didn't mean "typical" as in "most Rust fn signatures are like that", obviously most functions are far simpler. Who would even claim that?

I meant "you will only encounter this level of generics complexity in Rust" (disclaimer: I don't know C++). They can't be that rare either, I have not written a lot of Rust but already encountered similar constructs multiple times. But admittedly that might be partly due to no-std requirements.

Re:Understanding Rust futures by going way too deep by jen20 on Monday July 26, 2021 @02:21PM (#27963124)
C#'s `Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>` [1] would like a word!

[1[: https://docs.microsoft.com/en-us/dotnet/api/system.tuple-8?v...

Re:Understanding Rust futures by going way too deep by jcelerier on Monday July 26, 2021 @04:35AM (#27957098)
Generics are about encoding supplemental bits of information in the type system. That information has to be spelled out somewhere
Re:Understanding Rust futures by going way too deep by cormacrelf on Monday July 26, 2021 @04:43AM (#27957133)
Yes, however in my experience you typically have to write out constraints like these a bunch of times. Refactoring a generic trait with 10 implementations is a nightmare. A struct with a couple of variations on the constraints for its impl blocks is also painful beyond like one generic param. It has to be spelled out somewhere, but then at minimum somewhere else, and in general about 3-4 more times.

I see why Haskell folks like their type level functions.

Re:Understanding Rust futures by going way too deep by tinco on Monday July 26, 2021 @05:55AM (#27957554)
In Haskell I used to do this trick where if I wanted to neatly type annotate, but my function had some crazy signature, I'd compile the program, but passing the function into another function in an intentional incorrect way. The compiler would complain "expected A but got B". And I would simply copy B and make it the type annotation :P

Nowadays the editor would help you do that. When I'm coding Rust the editor shows the types inline in a small font, which is very helpful.

Re:Understanding Rust futures by going way too deep by myrrlyn on Monday July 26, 2021 @07:19AM (#27958083)
yeah, bounds written on the type decl shouldn't be repeated on the impl. the current typeck is not smart enough to do that but the ever-"nearly there we promise" replacement is. it'll land one day. they promise
Re:Understanding Rust futures by going way too deep by magicalhippo on Monday July 26, 2021 @09:25AM (#27959049)
I don't know any Rust, and that thing made perfect sense to me (compared to other things in Rust).

How would you have preferred it looked?

Re:Understanding Rust futures by going way too deep by richardwhiuk on Monday July 26, 2021 @04:45AM (#27957144)
You can do:

  pub fn try_join<A: Future<Output = Result<AR, E>>, B: Future<Output = Result<BR, E>>, AR, BR, E>(a: A, b: B) -> impl Future<Output = Result<(AR, BR), E>> {
Re:Understanding Rust futures by going way too deep by diragon on Monday July 26, 2021 @05:41AM (#27957460)
Rust truly is a better C++.
Re:Understanding Rust futures by going way too deep by Ygg2 on Monday July 26, 2021 @04:36AM (#27957103)
Do you have a better way to write the same constraints?

Conceivably if there was a way to not repeat the future parts.