Can we please be APL?
Thank you all for reading and consideration.
I've now spent many hours, narrowly focused on only the surface syntax question of this thread. Yes, I fully accept that there is much more to the full story of async#await than this syntax, and the design quality of those other language and API aspects are largely independent. I simply don't have time for research to constructively comment on any of the other aspects. That said, I do believe that syntax will play a significant role in the reception, success or failure of async#await in the long term.
I largely agree with @Pauan's sentiment, thus my surprise with the .await
syntax. I hope that if syntax is 10% of the overall design space, then the async#await design is 95% baked. That's really very good progress on a very important and complex feature!
Finally, no one asked, but I have one openly published crate using futures 0.1.x, tokio 0.1.x and hyper. I started a new branch of this project for an experience report with std::futures
, only to realize that that hyper doesn't have even an experimental branch, I could find, that works with std::futures
. It seems best to save my experience report for later, when things are actually usable. Maybe next year but hopefully sooner?
Without having that experience and knowledge yet, I am wary of and FWIW advise against at this particular point in time, any syntax requiring the commitment to new language productions, like «postfix unary keyword operators». Note this is my tactical opinion, in the long run, postfix unary keyword operators may be a great feature!
—David
You can use futures compat to freely switch between 0.1 and 0.3 (std) style futures. It adds a deal of .compat()
to your code, but it works today.
Thanks for that helpful hint. I think I tried that? I’ll followup with you elsewhere.
As an inexperienced, new user of the Rust programming language, I must say that I would be very surprised to learn that foo.await
is actually the invocation of an operator. From the programming languages I know, this syntax seems to be quite unusual.
While this syntax may be more ergonomic to use (although I doubt that endless chaining of await
calls leads to readable programs), it is certainly harder for newcomers to get by. As far as I understand, Rust already struggles with teaching new concepts but also new syntax to first time users. Hence I wonder whether this is the right path to go. A simple prefix operator requiring more parentheses but also less surprising syntax would imho be preferable for new users coming from different programming languages.
I think this compares very well to Python’s meaningful indentation. Nowadays, most people are so used to Python that they don’t question it anymore, but 10 years back, this was one of the most heard arguments against the language: nobody else does it, it’s weird, it’s confusing, newbies will never get it right, etc. Once you get used to it though, most people find the ergonomics to be fantastic, especially with some editor support e.g. for displaying indentation levels and shifting around blocks of code.
It’s also kind of similar to learning, say, Russian. People naively think the different alphabet is a huge hurdle, but it’s only 33 letters, which you can memorize easily in a few days, after which it becomes the same process as with learning any other (at least European) language.
NOTE: @CAD97 below is right, my argument does not hold. I think it can be dismissed. I still believe however that because if
and match
have brackets and a body, that still makes them different from await
. But now I’m back to my opinions and it won’t add much value to the discussion.
I’m unsure about the chaining argument (I won’t argue about the style preference, it has been done extensively already, and I tried very hard to find if that argument was made elsewhere)
I understand an argument for “.await” is that it’s chainable, which opens up possibilities for other keywords like “match”. However, I don’t see “.await” and “.match” behaving the same way.
“.if”, “.match” are actually only usable at the end of a chain, but “.async” can be used anywhere in the chain, exactly like a property or a method call.
It makes sense to say let x = something().await.property.something().await;
but it makes no sense to say let x = something().await.match.???
.
I, therefore, believe that even though there is merit to the postfix syntax, it shouldn’t affect the decision for the await keyword, since they will behave differently and be usable in different places in the code (unless you end up having a postfix syntax which specify different usage limitations based on context, which I’d disagree with but it’s not the conversation here)
Now for some thoughts about the .await syntax (I’m afraid it is similar to other arguments that have been made, but hopefully it’ll add value to the discussion since I think it’s not exactly the same argument)
I understand based on the RFC that the await keyword actually work on a Future, which makes it behave very similarly to a Trait. So I should be able to do something like:
let x = buildSomeFuture();
let y = x.await;
In that case, I have a Trait Future<Something>
, on which I can pause execution from the current function by calling something on it. In that case, since there is no chaining, it’s indiscernable from other struct members usage, and otherwise behaves exactly the same as a Trait function but without parenthesis. I understand the argument that you cannot write such a function yourself, but it’s also true for the “property” style.
So even though the .await
syntax clearly has arguments for it, I’d argue that a macro or a keyword (probably a macro that simply hides the keyword syntax) would be superior in terms of “learnability” and readability (hard to quantify though).
As an example, here the consistency being “broken” (sorry, the word is harsh but I hope no one will be offended) is that .await is the only case where side effects are possible, but without parenthesis.
let x = factory
.my_car
.tight_bolts()
.await
.expect("Wrong bolts?")
.tank
.inspect(|fuel| { println!("{}", fuel); })
.await
I’m fine with the postfix syntax for .match and .if, since they are followed by a code block, I don’t see how they can be confused. This is not true for the async keyword though, which is why I believe the postfix syntax should not be taken into account in how await is going to be used. Thanks for listening!
await
syntax needs to be considered apart from any postfix other keyword first. It needs to stand on its own.
That said, you can chain a postfix if
/match
, if said if
/match
returns a value, as all if
/match
can in rust. To rewrite your expect
as a postfix match
:
.match {
Ok(val) => val,
Err(err) => panic(
"Unwrapped on Err value {}; {}",
err, "Wrong bolts?",
),
}
Is this still true if we stop calling it Postfix syntax? Maybe we can just call it for example Dot operate syntax from now on?
Silly idea here (from a newcomer), why not just make it ..await
Yes, two dots. I guess that should be enough for disambiguation.
let x = buildSomeFuture();
let y = x..await;
- Obviously not a field access.
- Postfix
- No new characters
- No spaces
- Ergonomic
It could even be made into a three dots shortcut, which could help with language barriers.
let y = x...;
or chain it with method call
let y = x...someMethod();
As a bonus 3-dots makes it look you’re actually waiting for something, so it kinda makes sense.
Cheers!
Please, not three dots unless the Unicode 3-dot symbol …
is recognized as an equivalent. I for one am glad that the Lang team deprecated 3-dot inclusive range syntax.
BTW, the decision date has already passed, so such discussions are essentially pointless (pun intended).
If a thread is too long for you to read before posting in it, please at least use the "search in thread" tool to check whether your idea has already been suggested. In this case:
How about adding a prefix form of ?
(let’s say it’s the keyword try
) and using it with await like:
let resp = try await request("url");
The final decision has been made, so any more discussions or suggestions will have no effect, other than wasting everybody’s time.
(In addition, that suggestion has already been made before, because literally every suggestion has been made before.)
Well, the proposed syntax is in the nightly channel. So I guess the Rust team will monitor feedback closely for the next six weeks before promoting it to the beta chanel? After that, there will be another six weeks before the beta release becomes a stable release — but I'm not sure if beta releases are used for collecting feedback like this?
As far as I understand, the .await
not-a-field syntax is so new that it hasn't really been battle tested in non-trivial examples outside of this forum — until recently the await!()
macro was all people could play with, no?
So now that the real syntax is available, it must be the best time to actually see how it feels to use it in real code.
By the way, it would be good to wait until things like syntax highlighting is available for common editors. Otherwise, I fear that criticism of the readability of .await
will be met with a "don't worry, it'll be fixed when you have syntax highlighting". Such a dismissal would be very unfair since it relies on unknown future effects.
The purpose of beta is not to “try out” ideas and get feedback (that’s what nightly is for), the purpose of beta is to find implementation bugs before they reach stable (and to find regressions).
So any of the “battle tested real world examples” you mentioned will need to happen during nightly (which is right now).
This thread is not the place to discuss that, Niko created a thread dedicated to that. Of course we’d be glad to hear more experience reports (in that thread).
The decision was made. At this point, though async/await could be held up by someone finding a soundness hole, the syntax has been decided. The chance of the decision being changed is effectively zero.
The systems are the interesting and tricky part. The syntax is just a choice (though it does have impact) that needed to be chosen. It has, let’s respect that choice. The lang team does not have an easy job managing the flood of ideas and feedback they’re provided with.
I'm not sure where to jump in here. And normally I wouldn't jump in at all. But this... triggered me a little, I guess... the @Pauan post that sounded like pushing people away if they weren't there at the start of discussion. The effect I got was, "if you're too late you need to shut up". That's wrong, obviously.
I'm pretty impressed by Rust's standards of discussion. I don't feel that this one is meeting them.