23 ms·
A final proposal for Rust await syntax
- ch 7y agoNice insight into the amount of thought going into this design proposal. It's always tricky to introduce new syntax to a language and re-using the field access notation here isn't as icky an approach as it first looks. I don't know enough about rust to understand how this would operate during compile time w.r.t if a user tries to define a field named 'async'. Is that no longer allowed or would the compiler be able to disambiguate?
- lukeqsee 7y ago> Is that no longer allowed or would the compiler be able to disambiguate? I would speculate that both will be true. The compile could disambiguate, but it will not be allowed.
- jmcomets 7y agoDisambiguation is easy when `await` is a keyword. [0] ;) error[E0721]: `await` is a keyword in the 2018 edition --> src/lib.rs:2:5 | 2 | await: i32 | ^^^^^ help: you can use a raw identifier to stay compatible: `r#await` [0] https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b3999aad31966f91af75d501358d02bd https://play.rust-lang.org/?version=stable&mode=debug&editio...
- msla 7y agoProgramming languages didn't used to have reserved words: https://en.wikipedia.org/wiki/Reserved_word https://en.wikipedia.org/wiki/Reserved_word > Not all languages have the same numbers of reserved words. For example, Java (and other C derivatives) has a rather sparse complement of reserved words—approximately 50 – whereas COBOL has approximately 400. At the other end of the spectrum, pure Prolog and PL/I have none at all. I don't really know why modern programming languages bother with reserved words. Yes, it would be confusing to have a variable named 'if', but compared to all of the other ways to write confusing code in, say, C, that's barely a drop in the bucket. Plus, it's something good tooling (highlighting, for example) could obviate, as it's entirely possible to use a grammar to show exactly what role each token is playing in a statement.
- Nadya 7y ago>I don't really know why modern programming languages bother with reserved words Preventing potential footguns prevents a large number of bugs. In fact, preventing a specific kind of footgun is one of the primary reasons that Rust exists. Fewer footguns means easier to work with code. Easy to work with code means fewer bugs. If having reserved words removes more footguns than it creates than they are a good thing to have - and IMO - they do indeed prevent confusion and footguns. As long as these footguns can be removed without giving up much in exchange, I say remove all footguns!
- sinistersnare 7y agoI don't know about PL/I but Prolog kind of cheats, in my opinion, about the reserved words. It is very strict about naming conventions, which is (IMO) worse than having reserved words.
- msla 7y agoPL/I is syntactically like Algol, or Pascal, or C with more words and fewer brackets. At heart, it's a "normal" block-structured procedural programming language, which proves that a C clone could adopt the same basic idea.
- int_19h 7y agoXQuery is probably a more recent example of that. No reserved words there, just syntax. https://www.w3.org/TR/xquery-30/#nt-bnf https://www.w3.org/TR/xquery-30/#nt-bnf
- moosingin3space 7y agoMy guess is the raw identifiers syntax introduced in Rust 2018 (`r#` prefix) will handle this.
- erk__ 7y agoIf you want to use async or await as a fieldname you have to write r#await and r#async because they are reserved in rust 2018.
- mqus 7y agoafaik, as of Edition 2018, await is a keyword and one of the few(2?) reasons that the 2018 edition is not backwards-compatible with 2015.
- wyldfire 7y ago`await` was changed in rust 2018 to a keyword [1] and will now get a warning if misused IIUC. I'm guessing `async` keyword only shows up in contexts where identifiers aren't expected, so there's no ambiguity? [1] https://github.com/rust-lang/rust/pull/54411 https://github.com/rust-lang/rust/pull/54411
- drop_the_mike 7y agoIf I'm not mistaken you couldn't have ever had 'await' as a field name because it's a reserved keyword. You can still use it by using the raw string syntax r# and that disambiguates when you access it to because it would be called like: expression.r#await
- lukeqsee 7y agoI'm anxiously awaiting this. I just finished overhauling a rather involved Tokio/Future based project, and having await/async in the language spec will be a big step forward (and I'm hoping the compiler errors improve as well). As always, kudos to the Rust team for making decisions that are well-reasoned and documented and in a way that looks to the future of the language (and not just meeting a feature deadline)!
- astrodust 7y agoIf the way async/await has fundamentally changed how you write JavaScript code is any example, this will be a tectonic shift, especially for event-driven code like you use with Tokio.
- kibwen 7y agoI agree that it's an important addition to the language, but I think it will be less impactful for Rust than for JavaScript because server-side JS is almost always in a domain that benefits from async IO, whereas Rust exists in many domains where async IO isn't a performance priority.
- astrodust 7y agoIt's an important concurrency model. Perhaps Rust doesn't have a lot of async code because it's been really annoying to do it. This could change that dramatically.
- majewsky 7y ago> I'm anxiously awaiting this. Pun intended?
- cesarb 7y agoTo me, this syntax feels like a "magic" method call (like in languages where a field access can go through a getter method). That is, if you can think of "x.await()" as a compiler-implemented method call on x which does some stuff and returns a value (plus some magic to allow you to omit the parenthesis), this syntax looks intuitive enough.
- Sharlin 7y agoExcept that, as described in the article, `await` is fundamentally a control flow construct and too magical to be ever considered a ”sort of a method call”. It’s not really a useful mental model. No function is supposed to be able to reach outside its definition and rewrite the control flow of its caller.
- nchie 7y agoWow, this is the only suggestion I really didn't like. I'm really happy that it's progressing forward, but I can't help but feel that staying with the macro and postponing the decision would've been a better choice. A language should, in my opinion, not depend on syntax highlighting.
- Arnavion 7y agoWhat good will postponing do? The blog post implies that it's already been debated for a long time and all the arguments seem to have already been brought forth. You can't just keep punting it forever; it has to get stabilized at some point.
- nchie 7y agoIt's been debated, but there's hardly any code using it. After seeing how it's being used by most people, it'd be easier to make a choice.
- steveklabnik 7y agoBecause it’s been clear that what exists is not stable, many people are waiting until it’s stable to try. There were a number of people who converted code to the various proposals, so that helped.
- oconnor663 7y agoI think it was a tricky situation where the option with the "fewest/weakest haters" wasn't the same as the option with the "most/strongest likers". I think restricting ourselves to options that don't generate too much hate is one of the reasons why "design by committee" tends to produce disappointing results.
- comex 7y agoI think concerns that it depends on syntax highlighting are exaggerated. Compare `.await` to the `?` operator, both of which indicate a change in control flow. `?` does have the advantage of being a symbol which looks visually distinct even without syntax highlighting – but `.await` is much longer and thus harder to overlook entirely. Also, in practice `.await` will often be immediately followed by `?`, since async operations tend to be fallible. Since using `?` directly on a field of something is very uncommon, `.await?` acts sort of like its own unique syntactic construct with a unique visual shape, helping it stand out further. That's assuming that you know the syntax already. If you're learning the syntax for the first time without the benefit of syntax highlighting, `.await` might be more confusing than it would otherwise be – but I think the point about `.await?`, combined with the familiarity of `await` as a special operation from other languages (as well as it just being a verb rather than a noun), would strongly hint that something different from a field access is going on. If not... well, you can always learn what it does from the documentation, since it'll be one of the first things explained in any kind of async tutorial. I'm a big believer in the concept of "strangeness budget" and avoiding unnecessary syntactic unfamiliarity, but I think the strangeness is very strongly mitigated here. In any case, most people will learn the syntax with the benefit of syntax highlighting, and IMO taking advantage of that is much more defensible than depending on syntax highlighting for readability more generally. From a historical perspective, in the debate leading up to the switch from `try!()` to `?`, people were worried that `?` would be lost in the noise within long chains, an issue that others said would be mitigated by syntax highlighting. As it turned out, syntax highlighting does help, but `?` is also pretty distinctive without it. I think `.await` will pan out similarly. More generally, I think that once people get used to the idea of `.await` and it stops being this new weird-looking thing, the mere-exposure effect[1] will subside and people will stop seeing confusion with field access as such a big problem. Well, people already using Rust, at any rate. Ironically, once you get past unfamiliarity, the reverse bias comes into effect, where it becomes unintuitive to think how other people first learning the language will find something weird. But from that perspective, there are a lot of weird things in Rust's syntax already. Adding more is not a good thing, but it's also not as bad as it might seem if everything in Rust seems familiar except this one thing. [1] https://en.wikipedia.org/wiki/Mere-exposure_effect https://en.wikipedia.org/wiki/Mere-exposure_effect
- james-mcelwain 7y agoReally don't like the magic field access syntax.
- lukeqsee 7y ago> Really don't like the magic field access syntax. From a language-feel and ergonomics standpoint, I agree. But the Rust team has made it pretty clear why a postfix is much more flexible, and I think we'll grow into it. I do think it'll continue as a bit of a Rust oddity when compared to other async/await implementations (e.g., Python).
- apk-d 7y agoHaving done most of my work in C# and TypeScript the Rust syntax felt weird when I read about it, but on the other hand, I always felt bad being unable to readably chain awaits, least they become var result = (await (await obj.DoSomething()).SomeOperation()).SomeValue; In the end, all syntax is magic that you need to get used to, I guess.
- wbl 7y agoDo notation solves that problem!
- IngoBlechschmid 7y agoCame here to say that. I think do notation (and applicative idioms) are easily one of the most-underappreciated features outside the Haskell community.
- yawaramin 7y agoScala has had monadic do-notation (called 'for-comprehensions') for a long time and OCaml has recently gained both monadic and applicative 'do notation' (called various things, but mostly 'let+ syntax'). Edit: Oh and F# has also had computation expressions for a long time.
- 7y ago
- jules 7y agoThe designers of Kotlin also had an interesting point: await (synchronous call) should be the default, non-awaited (asynchronous) code should have a keyword indicating that it is async.
- saghm 7y agoIt's worth pointing out that `await` is only needed in functions explicitly marked as async; `await` is the default for non-async functions (which is the default mode for a function to operate). I think the rationale behind this is that it's fairly common to need to group a bunch of async operations together, and this is easier to do if you can mark an entire function as async rather than needing to mark individual statements.
- jillesvangurp 7y agoThe Kotlin people came up with a few useful notions around co-routines: - functions that can be called asynchronous must be marked with suspend. - functions marked as suspend can only be called from within a co-routine context. This is an abstraction that gives you a handle on resources consumed by your co-routines and some level of control over that. - You can create/get a co-routine context in several ways and there's a global context (i.e. the main thread). Useful other contexts could be some web request or some thread pool. A context has dispatcher, a scope, and a few other things. 3) suspend functions calling other suspend functions implicitly await each other, i.e. preserve before/after semantics. It looks like normal code and there are no special keywords needed. IMHO this is genius compared to promise chaining and error handling you deal with in javascript which can become quite messy. Even with async await in js, you still need to return a Promise. In Kotlin all this is implied by using the suspend keyword on the function. 4) await is indeed something you do explicitly in a synchronous function only and it blocks the thread it is happening on. So, it's also something you should mostly avoid. 5) Co-routines can be terminated. This ensures that any still running async calls stop wasting cpu time. This is a problem in e.g. javascript where once you are awaiting something, you have no way to interrupt whatever it is you are awaiting. 6) pre-existing other asynchronous stuff in Java and its various frameworks can be adapted to co-routines quite easily. This is a complicated topic and I'm sure there are a thing or two here that don't quite map to Rust that easily, which they've probably debated at length. I imagine tight memory and resource control is important for Rust. But it's a nice design for Kotlin at least. Compared to Rust, the development process was interesting as well. There was a long experimental feature cycle (1.1 and 1.2) during which you could opt into using it but during which there were also major changes based on the usage and feedback. I think they learned a lot during this phase. There are still new things coming that are still experimental (e.g. channels).
- Dowwie 7y agoI trust that they've made the best decision.
- cyphar 7y agoSuper excited to see async/await finally get close to MVP and landing. I am not a huge fan of ".await", but there isn't much more to be said -- my personal preference of "#await" or "@await" does look like line-noise and I think there's no perfect answer to this one (await{} was too messy and no better than prefix-await, and (await foo))? had too many brackets). I also appreciate that this proposal was insanely bike-shedded and so really, any decision is better than no decision. I would've been happy with "await << f()" if it meant we could get this feature (lots of Rust projects I'm interested in are waiting on async/await before focusing on further development).
- blattimwind 7y agoWouldn't "await expr" or "await@expr" (if you hate whitespace) make more sense compared to "expr await"? let db_conn = await pool.connect() (Which is what e.g. Python uses, with the downside that you tend to need to parenthize more because await has very low precedence). vs let db_conn = pool.connect() await .await is not soo bad, kinda method-y let db_conn = pool.connect().await
- eslaught 7y agoDid you see the previous post on this topic? They discussed this extensively: https://paper.dropbox.com/doc/Await-Syntax-Write-Up--AcIbhZ1tPTCloXb2fmFpBTt~Ag-t9NlOSeI4RQ8AINsaSSyJ https://paper.dropbox.com/doc/Await-Syntax-Write-Up--AcIbhZ1...
- cyphar 7y agoI don't think "expr await" would be a good choice but that isn't the decision that was made, and I wasn't arguing for it in the first place. TFA says that using alternative punctuation was decided against because it would lead to line-noise and I don't think there's much more to discuss -- I understand that position and respect it. My main issue with .await is that it does look method-y rather than keyword-y. But while I like Python's "await expr" syntax in Python the existence of ? and chaining of methods in Rust justifies having a different syntax for it.
- nixpulvis 7y ago> Its very easy to build a mental model of the period operator as simply the introduction of all of these various postfix syntaxes: field accesses, methods, and certain keyword operators like the await operation. (In this model, even the ? operator can be thought of as a modification on the period construct.) I like this explanation. But this whole post could really use some more concrete examples of the at the very least the final style being used in various situations. Not just mainly `expression.await`. Keep up the work on this, I'm excited to use this in my projects.
- Sharlin 7y agoMy dislike for the ”dot keyword” syntax has been strong, but if it is selected, hopefully it’s at least generalized to other keywords in the future, so that `.await` doesn’t stay a lone awkward exception to what dot notation means. In particular, I wouldn’t mind if in the future `expr?` could be spelled `expr.try` for consistency (even at the expense of introducing redundancy).
- saghm 7y agoI like that idea for try! Near the bottom of the blog post, they mention that `match` could become a "dot keyword" expression, giving the following example: foo.bar(..).baz(..).match { Variant1 => { ... } Variant2(quux) => { ... } }
- faitswulff 7y agoThe post says as much: > In particular, some members of the language team are excited about a potential future extensions in which some “expression-oriented” keywords (that is, those that evaluate to something other than ! or ()) can all be called in a “method-like” fashion. In this world, the dot await operation would be generalized so that await were a “normal” prefix keyword, but the dot combination applied to several such keywords, most importantly match:
- Sharlin 7y agoYeah, that’s what I referred to, just wasn’t very clear.
- jnetterf 7y agoWhile there might be a better syntax if you only care about this one feature, async/await needs to fit into an existing language, and this syntax makes sense with the rest of Rust. This syntax makes it clear that’s it’s not a function or macro invocation, works the same way as ‘?’, and allows for clear and concise chaining.
- jerf 7y ago"It has also devolved into a situation in which many commenters propose to reverse previous decisions about the design of async/await on which we have already established firm consensus, or otherwise introduced possibilities we have considered and ruled out of scope for now. This has been a major learning experience for us: one of the major goals of the “meta” working group will be to improve the way groups working on long-term design projects communicate the status of the design with everyone else." I'll be pretty intrigued to see what they come up with for that. I've been up closer to the Go developer's attempt to connect with the community, and while I'm not going to say they've done everything perfectly on their end, I've also been sort of frustrated by the way that they'll put up a request for comments about something in particular, and a good chunk of the community replies basically throw away everything they're talking about and rewrite arbitrary amounts of the language from scratch. By "everything", I don't just mean the direct proposal under discussion, but the goals of the proposal, the discussion of alternatives from other languages and how they are and are not relevant, as alluded to above the things already proposed and rejected for solid reasons, the context around the runtime and how it interacts with that... that is, not just the proposal itself, but all the thought and reasoning around it too. That wasn't really the question, so to speak. This frustrates everyone, on all sides, in various ways. A lot of the problem is structural to what is being done; there's a lot of impedance mismatch between the core designers and the community at large for any language. It would be interesting to see some explicit thought around how to address that, from a community with Rust's experiences.
- wbl 7y agoFrom what I've seen of Scheme they dont have this problem. Everyone implemented the feature they want or forks an implementation. And no one argues about syntax.
- jerf 7y ago"And no one argues about syntax." No one argues about the syntax, because everybody "wins" and gets their own syntax as a result, which is often held up as a key part of the explanation for why the Lisp family languages are wonderful and fun and mind-expanding and just awesome in every way... yet rarely escape from "niche" status and are yet to even threaten to break into the really top-tier languages. Because languages like Rust and Go, and Python and Java and honestly almost every other non-Lisp family language, want more cohesion in their codebases, they need a different solution to the problem.
- erikpukinskis 7y agoReading this feels like a person with no good choices trying to convince themselves there's no other way than their best bad one. "This is the best proposal, except the syntax doesn't make sense, we don't know if we can implement it, and conversation has broken down to the point where we are running in circles and we don't expect to have any more ideas." I am curious what the current way to do non-blocking code in Rust is and why it's so bad that they'd introduce this much confusion to the language design to fix it.
- steveklabnik 7y agoIf every choice was obvious, there would be no need for a designer in the first place.
- erikpukinskis 7y agoWho said any choice was obvious? (Also would love your take on the question in the last line of my comment. My gut as an outsider is that the best solution may be cultural not technical, except Rust is struggling to implement cultural projects now that the community is scaling fast. But you know what I don't.)
- steveklabnik 7y agoHeres the most straightforward answer I can give: http://aturon.github.io/2018/04/24/async-borrowing/ http://aturon.github.io/2018/04/24/async-borrowing/
- dj-wonk 7y agoI am unfazed by this observation. When it comes to design, sometimes ‘perfection’ isn’t available. By the way, the idea you mention may also be true for democracy: the quote goes something like “Democracy is the worst form of government except for all the others.” If you have a design option that has not already been considered, I’m all ears.
- whatshisface 7y agoThe best thing they could have done was to have arbitrarily picked a syntax months ago - but of course, at the time nobody knew it would be so difficult to determine the "right way" that choosing arbitrarily would be the best option. It raises my esteem of the Rust lang leadership that they eventually realized what they had to do and picked something.
- foota 7y agoIt seems they've settled on using a postfix approach. I can't help but feel this is a mistake. Rust is already a weird language to come to from the likes of python, Java, or Javascript, and it feels to me like this relatively unknown approach of putting the operator at the end is a mistake that will add another confusing aspect of the language. I feel like the committee has worried about the wrong things when considering postfix vs prefix. Their main concern seemed to have been the extra syntax required to make prefix work with . And ? (i.e., (await foo).bar() ), but while avoiding this makes for a more optimized language I'm concerned they've missed the impact this will have on new people. Or maybe I'm just a Luddite :)
- wtetzner 7y ago> Rust is already a weird language to come to from the likes of python, Java, or Javascript I think that's a great reason to evaluate new syntax/features on how they will be used, as opposed to how they will attract (or not) new users. New users are already going to be expecting things to be different, and so making this one thing more familiar probably won't make much difference. On the other hand, code lives for a very long time once it's been written, so making the right choice in terms of readability/maintainability seems like the right move.
- foota 7y agoWhile that's true on an individual basis you can't use that continually to introduce differences, because at some point if someone comes to rust and sees a heap of weirdness they're going to be less likely to adopt the language. I'm mostly a bystander since I've written some rust but not very much, but I think rust is neat and I don't want it to become an esoteric language.
- ljm 7y agoIf it's an effective solution it will only seem weird until it's proven to be more useful than the status quo approach. I read this post and thought 'async as a prefix is intuitive!', but that's only because I saw it first in C#. Rust already has an immense learning curve despite its ergonomics, I say it should continue to experiment. Same way Haskell does. I do wonder if `.await` could have a sigil equivalent the same way `try!` did with `?`. `…` could work if it's not already used as spread args.
- lachlan-sneff 7y agoI'm curious as to why the mandatory prefix syntax wasn't chosen (`await {...}`). It's less magic than a magic field and it fits into preexisting syntax better.
- frenchy 7y agoI think it has to do with "ergonomics", and the existance and interaction with the "?" keywords played a big part of that.
- bvinc 7y agoI'm not sure why it wasn't chosen, but I can give you some arguments against it. The braces introduce line noise. They introduce a new scope. I believe that rustfmt will currently put a newline after the opening brace. And it has the general problem that it can't be read from left to right. (P.s. I'm not trying to start a debate, just trying to answer the question.)
- int_19h 7y agoTry writing a chain of async member accesses and calls with . and ? like that.
- mklein994 7y agoThe syntax feels a bit Ruby-ish, doesn't it? Here's an example from the article of how they could expand the syntax in the future: foo.bar(..).baz(..).match { Variant1 => { ... } Variant2(quux) => { ... } } Compared to some Ruby: 5.times { puts "Hello world!" } Note I'm by no means an expert in Ruby or Rust, this is just what I thought of when I saw the syntax.
- hombre_fatal 7y agoA .match "method" just opens the dam to "why stop there?" I prefer Kotlin's general approach of .let and similar: number = foo.bar().baz().let { match it { A => 1 B => 2 } } Now anyone has the general tool for chaining without needing library authors or language designers to create the API for them.
- jnetterf 7y agoNeat! When working with Options/Results/Iterators, you can use `.map` [1] for exactly this purpose. It would sometimes be convenient to have something like `.map` / `.let` on unwrapped values as well. [1] https://doc.rust-lang.org/std/option/enum.Option.html#method.map https://doc.rust-lang.org/std/option/enum.Option.html#method..., https://doc.rust-lang.org/std/result/enum.Result.html#method.map https://doc.rust-lang.org/std/result/enum.Result.html#method..., https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map https://doc.rust-lang.org/std/iter/trait.Iterator.html#metho...
- centril 7y agoNote that in Rust, `.match` would itself provide the general way: let number = foo.bar().baz().match { it => it + 1 };
- pitterpatter 7y agoRe: your second example, once upon a time you could write something very similar in Rust: do 5.times { // ... } Where do was just sugar for calling functions that took a closure as the last argument: fn foo(a: A, ..., z: Z, cl: ||) { ... } foo(a, ..., z, || { ... }) do foo(a, ..., z) { ... }
- holy_city 7y agoThe process for how this feature is being handled is fantastic, and a great example of what I love about Rust and its community.
- arcticbull 7y agoSerious suggestion, and I assume you have -- have you considered ".await?" as an alternative? It won't conflict with field names and cribs on the fact that "?" changes control flow.
- steveklabnik 7y agoYes, it was.
- eridius 7y agoThat conflicts with `.await` + the ? postfix operator. Namely, if `.await` returns a Result, the ? operator could then be used like it is in code today. In fact, this was referenced in the article: > The primary argument in favor of postfix was its better composability with methods and the ? operator.
- arcticbull 7y agoYeah, I thought that through after, and revised my thoughts. I wrote it up top-level but I think the best way forward is embracing that this isn't consistent with anything else in Rust and introducing new syntax, specifically '!await' or '@await' postfix operator. The reason there isn't a good answer that's consistent is that the behavior is inconsistent. As such, it needs new syntax. Either the syntax is inconsistent or the semantics are, and IMO, the former is preferable to the latter.
- tav 7y agoIn the off chance that some Rust developers are looking at this thread, I'd like to put forward a counter-proposal: 1. Use a postfix ?! instead of .await 2. Use a postfix ?? instead of .await? 3. Use the await keyword only in the "for await" construct Then the common case becomes: let resp = http::get(url)??.to_string() Which, imo, is a bit easier to parse than: let resp = http::get(url).await?.to_string() This would make it easier to follow the core logic in async code, the same way that ? made error handling so much cleaner in Rust.
- nixpulvis 7y agoThe `!` already means "diverges", I don't love the re-purposing of this here.
- afiori 7y agoI think the ?? can be already used for Result<Result<_,_>,_>. I agree that the syntax should have `await' somewhere, and the observation that it "clearly" is not a field access is actually credible to me. This also open the possibility to have other kinds of postfix keyword e.g. .try or .match
- Grollicus 7y agoAt this point I don't really care how the syntax looks. I just want to use it, been waiting soo long for this. Problems with differentiating .await from .member can easily be solved by using syntax highlighting. Also, Rust is already strange to write, this is just one more quirk one will get used to.
- jkarneges 7y agoI figure the reason the proposed syntax looks gross is because other languages have been using a prefixed await for many years. The Rust decision seems well thought out though, enough to make me wonder if perhaps other languages have been doing it wrong. My first experience with futures was in the form of QFuture (https://doc.qt.io/qt-5/qfuture.html https://doc.qt.io/qt-5/qfuture.html), and there you call .result() to block and wait for the result. This postfixing is intuitive. Rust's "?" operator is neat. I don't know if I've seen a postfix operator anywhere else yet. It's certainly possible Rust got this language aspect wrong, but as a user it feels pretty right. Given the existence of the "?" operator, and the fact that futures resolution via postfix is intuitive (and not a new thing either), IMHO the best course of action probably is postfix. I'm not sure if literally ".await" is the best, but it's in the ballpark.
- slezyr 7y ago> I figure the reason the proposed syntax looks gross is because other languages have been using a prefixed await for many years. The Rust decision seems well thought out though, enough to make me wonder if perhaps other languages have been doing it wrong. Lua[1], C++/Boost[2] also use "yield" it's not that uncommon and there is nothing new. [1] https://www.lua.org/pil/9.1.html https://www.lua.org/pil/9.1.html [2] https://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/overview/core/spawn.html https://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/ov...
- int_19h 7y agoIt's not the question of keyword chosen, but rather where it should go. I wrote a lot of async/await code in C#, where it's a prefix operator, and I think what they did makes more sense. C# syntax works great when all you're doing is awaiting on a single call per line, but as soon as you start doing e.g. method chaining, you have to put parentheses everywhere, and it gets real messy real fast. This looks cleaner.
- M2Ys4U 7y agoAt first I was firmly in the prefix camp, until I read one of the (massive!) GitHub issues on the subject and came around to postfix. I'd _much_ rather use something like `?` than `.await`, though. Using a keyword just feels wrong in a few different ways.
- jnordwick 7y agoThey dismiss the best option - choosing another symbol instead of a period with hardly a though because they don't lke "line noise". Just as `?` was introduced, I don't see a good reason ("line noise" doesn't even count as a thought) that they wouldn't introduce postfix notion with `@` `#` `$` or any thing else similar. If they are looking forward to expanding postfix synax for things like `match`, then this would provide them with the most flexibility and essentially provide a new namespace for those features. "Line noise" isn't a reason. APL had it right in that consistent syntax (Perl doens't count as consistent) is useful.
- eridius 7y agoIt was glossed over why `await expr` isn't in the running; I take it it's because the language team doesn't like the necessity of adding parentheses any time you want to do postfix expressions on the await results e.g. `(await foo()).bar()`?
- Rusky 7y agoThat was discussed in a previous post, but yes that's why. And in Rust, the biggest postfix expression is the extremely common `?` operator, expected to be used with most futures.
- eridius 7y agoI haven't really been following along very much. I take it from your comment that await in Rust will not automatically propagate errors upwards like it does in JavaScript? So you have to use `foo.await?` in the normal case if you only want to handle successful results?
- wwwigham 7y agoI wonder why similarly to the existing `yield` keyword operator in the experimental generators support doesn't rate highly in the decision making here. Maybe they plan to revise generators to `expression.yield`, too? Also `expression.return`? Control flow from a dot-access rubs me just a bit the wrong way - honestly, I'd rather the syntax be a bit cumbersome because if I saw multiple nested `await`s in a single expression, I'd think the code was suspect and review it more, while I'm probably not going to have quite the same reaction for `x.await?.await?.foo.await?`, just because it _looks_ like normal property chaining.
- arcticbull 7y agoAfter reviewing the syntax writeup, I've reached my own decision on what color the bikeshed should be. I like the "Unusual other syntaxes: Use a more unusual syntax which no other construction in Rust uses, such future!await." [1] (or future@await). It makes sense because it is in fact something different than what exists anywhere else in Rust so it should receive a commensurate syntactic treatment. This was written off pretty quickly in [1] but it appears the most consistent with language philosophy -- specifically because it's inconsistent with any other language features it should get inconsistent syntax. I think that's what's throwing people off in this process: an attempt to impose 'consistency' on something that just isn't. As such, it'll never feel natural to co-opt existing syntax. Let's embrace it's inconsistency and introduce new syntax. It supports '?' and '.' natively, and offers a path forward for new similar kinds of postfix operators without making more field names off-limits -- even the expr@match { .. } postfix expression thrown around would fit well. The @-prefixed postfix operator would return a value like all other expressions. tl;dr: Either the syntax is inconsistent or the semantics are, and IMO, the former is preferable to the latter. Either way, cool to see this feature moving along! Looking forward to using it no matter how it's spelled out haha. [1] https://paper.dropbox.com/doc/Await-Syntax-Write-Up--AcIbhZ1tPTCloXb2fmFpBTt~Ag-t9NlOSeI4RQ8AINsaSSyJ https://paper.dropbox.com/doc/Await-Syntax-Write-Up--AcIbhZ1...
- OtterCoder 7y agoI'd tend to agree with you, except for the fact that Rust reserved words are reserved everywhere. Even if the syntax were changed, you still couldn't name a field "async" or "return". Since it's no extra burden on the namespace, I'm less opposed to it. I'm on the fence about disambiguation via unique punctuation, vs avoiding Perl-like line noise.
- ketralnis 7y agoHere's a question that may sound a little naive but I'm behind on where the Rust async stuff is shaping up and I don't know what to search for to answer it. You can of course already do async things in Rust the same way you do in C: by passing around function pointers and either passing around a handle to the event loop or, more commonly, using a single global handle. But you theoretically could run multiple event loops (one per core, say) and could pass around a pointer to which event loop you wanted to work with and a lot of libevent examples do this. (IMO it's the only correct way to do it, but I have sympathy for wanting to have the global variable.) This is a little different to Javascript where the event loop is part of the language/runtime. Javascript's "the event loop" model means you can't run more than one event loop per runtime, so it's kind of nonsense to want to pass it around explicitly. Rust is more like C in this regard. So if I wanted to mix threads and events, or run an event loop per core, or have an event loop that my Fancy HTTP Library manages itself but exposes a blocking interface to, do I lose access to the new fancy `.await` syntax? There's nowhere in this syntax to say which event loop I'm registering myself with. Is there just one global one? Does it get globally registered on runtime boot, and everyone references it? Or is there a thread-local variable for the "current" event loop that this thread knows about? Even more concerning is how to make _other_ libraries do this than the one I'm writing. If I'm calling into an async library how can I tell that library that I want it to use "an" event loop instead of "the" event loop? What should I be googling for to answer this? :)
- steveklabnik 7y agoYou do not lose access to it. A short version of the answer is that at its core, you create a chain of futures, and then place them on an executor. The executor is responsible for executing them. Async is sugar for creating a chain of futures. Doing that stuff is a property of the executor. Completely different axis. (And tokio today has a multi-threaded, work-stealing executor, for example.)
- ketralnis 7y agoI see, so the "root" Future in the chain is bound to an executor and that's where the "which event loop" part is specified
- lucideer 7y agoThis is way way off-topic but: I have never seen the word "postfix" used in this way. "Suffix" is the normal/common English word for this as far as I've always been aware. A quick search has it listed it as a synonym, but I can't find any usage outside tech and it sure seems like a tech-industry/coding erroneous neologism trying to balance the seemingly logical "post" -vs- "pre". Am I way off the mark here?
- dj-wonk 7y agoYes, "postfix" is commonly used. See http://www.cs.man.ac.uk/~pjj/cs212/fix.html http://www.cs.man.ac.uk/~pjj/cs212/fix.html or https://en.wikipedia.org/wiki/Reverse_Polish_notation https://en.wikipedia.org/wiki/Reverse_Polish_notation
- lucideer 7y agoReverse polish notation is an entirely different usage: not the same meaning as used in this proposal. It's also commonly used as the name of a piece of email software, but that's also an unrelated usage to this.
- dj-wonk 7y agoNo. RPN and postfix notation refer to the same core idea. From Wikipedia: "Reverse Polish notation (RPN), also known as Polish postfix notation or simply postfix notation, is a mathematical notation in which operators follow their operands, in contrast to Polish notation (PN), in which operators precede their operands." Can you please explain why you wrote "Reverse polish notation is an entirely different usage"? With a citation, preferably.
- lucideer 7y agoReverse polish notation is a term used in mathematical notation for an a alternative operator syntax to the more common "infix" notation. The article describes affixing the word "await" to methods in a programming language as a "postfix" and explicitly contrasts it with a "prefix", which is a linguistic term for affixing words, commonly contrasted with the term "suffix".
- andrewflnr 7y agoI'm sold. This post did a good job of addressing my concerns with the syntax, plus a couple others. I'm now looking forward to postfix match. I still kind of want postfix macros, though. I saw a couple other neat use cases like `file.write!(...)`
- Buttons840 7y agoUser definable postfix macros might be nice, I don't know. But we don't need user definable postfix macros to have a single `expression.await!()` as a magical macro. There are already magical macros, and to most Rust users `foo!()` means "magic here, read the docs". It would have been syntactically consistent to have `await!()` also mean "magic here, read the docs". 100% of Rust users will recognize the inconsistency of overloading field access syntax. Only a portion of Rust users will dig deep enough into macros to recognize that `expression.await!()` is special and is not defined as a normal macro. By the time a user digs deep enough to realize that, they are ready to understand why that inconsistency was necessary. With `expression.await` new users may ask about the inconsistency and the answer will be "because reasons you can't understand right now".
- cryptonector 7y agoDon't forget to consider dot-dot-await (EXP..await)as well.
- danfo 7y agoSince `await` is a keyword reserved for this purpose, could `(await expression)` ever mean anything else? What is the cost of ripping off the bandaid and starting there, as at least an alternative syntax that is consistent with the language? match expression await expression It feels like a future proposal, to allow these keywords to be chained more conveniently with a postfix. It's cool that this can possibly be generalised for chaining ergonomics. expression.match expression.await expression<-match expression<-await
- estebank 7y agoFor what is worth <- is already taken by the placement in syntax, which has been pulled back but rustc needs to continue parsing it to avoid breaking existing code which used it behind cfg flags. It doesn't do anything but it has to be understood. It would be possible to resolve that problem, but it's another thing to be aware of.
- kibwen 7y agoI expect a proposal of this nature to pop up relatively soon. If I had to guess, I'd say the reason they're not doing this now is because they only want to choose one option for the MVP, and they've decided that they prefer this option to that option, and that the chosen option fortunately does not preclude the future option. Also, I would expect `await` to have mandatory braces, just as `match`, `if`, `for`, etc. do.
- lkjalkjsdfasds 7y agoPostfix works because Rust doesn't have piping or do-notation. I don't care either way though, I'm welcoming await to the family & excited to use it.
- marvelous 7y agoI wish more language constructs used postfix notation. It looks so natural to me when reading from left to right. Consider: [a+2 for a in as if a > 3] Vs as.filter(a -> a > 3).map(a -> a + 2) Sometimes I even wonder why variable assignment has to precede the assigned expression.
- kibwen 7y ago> Sometimes I even wonder why variable assignment has to precede the assigned expression. I think there's a good discussion to be had on preferring either prefix or postfix operators, but the answer to this question (which is also the answer to the question "why are we so accustomed to prefix keywords?") is easy: because ALGOL did it.
- kccqzy 7y agoHardly natural because the site where the binding is introduced follows the expression that uses the binding. When you type from left to right, editors will highlight that unknown binding until after you bind it.
- Const-me 7y agoLanguage syntax and futures is only a half of the problem. IMO actual async IO implementation is harder. Too many incompatible platform-specific APIs: epoll and AIO on Linux, kqueue on BSD and OSX, IOCP and new thread pool API on Windows. I’m pretty sure I forgot couple others, and each of them have non-trivial amount of quirks, bugs, and performance-ruining edge cases. Also these APIs are not directly compatible to each other. To be usable, async IO needs to be integrated into the rest of IO. You can’t just place that on top, e.g. Java did that, IMO didn’t work particularly well. The combination of the above makes creating good cross-platform abstraction for async IO challenging. Not saying impossible, but it’s very hard to do. I’ve tried once in C++, for that project I needed epoll and iocp, but I wasn’t able within reasonable time. Ended up swapping relatively large IO-related parts of my app depending on platform.
- bvinc 7y agoMost of the work you're talking about has been completed for a long time in the crate "mio". It abstracts out all of the platform dependent async io operations into a single consistent interface.
- Const-me 7y agoAre you sure about that? I've looked at the library, it doesn't support anything besides epoll and IOCP, i.e. no BSD or OSX support, no AIO on Linux (the kernel one, not POSIX). You probably don't want to consume IOCP API on Windows anymore, Vista introduced higher level and easier to use StartThreadpoolIo and friends. Also on Windows, even with IOCP, you don't want to split async IO from thread pool. The OS kernel manages them both at the same time. Work stealing or other custom scheduling is usually slower than what kernel does.
- bvinc 7y agoIt says that it is also backed by kqueue and supports FreeBSD , NetBSD, and OS X. I found this issue where they're discussing rewriting the Windows implementation using the library `wepoll`. https://github.com/tokio-rs/gsoc/issues/3 https://github.com/tokio-rs/gsoc/issues/3 What would be the advantages of supporting AIO on Linux?
- trashface 7y agoAs an old salty programmer, I don't understand why people are upset at this. Sure the syntax is unusual, but they had good reasons for it, and people are adaptable and get used to things. Personally I like the fact that ".await" emphasizes that you are dealing with something fundamentally different from other language constructs, because futures _are_ fundamentally different.
- deleted 7y ago[deleted]
- danielscrubs 7y agoI'm very conflicted. In one hand I'm happy that becoming practical, on the other hand: await and async is the best these awesome PhDs could come up with? They where really in the forefront in so many regards that I kind of expected something mindblowing.
- mruts 7y agoWhy doesn’t Rust just implement HKT and then make a Future a monad. It seems to me that Rust has too many special things that arise from a lack of expressiveness as it is. HKT would make things considerably nicer.
- steveklabnik 7y agohttps://twitter.com/withoutboats/status/1027702531361857536 https://twitter.com/withoutboats/status/1027702531361857536