5 ms·
`fut.await()` looks too magical. There's no hint in that expression that it's anything other than a method call. You could argue the same about `fut.await`, exc
by eridius 7y ago
`fut.await()` looks too magical. There's no hint in that expression that it's anything other than a method call. You could argue the same about `fut.await`, except in this case `await` is a keyword and thus the expression `x.await` is known to be an await expression without knowing anything about `x`, whereas `fut.await()` requires knowing the type of `fut` to understand that the method call `.await()` resolves to the lang item. Also, since it's a lang item, you could write your own nostd library that picks a different name for the same lang item and cause even more confusion.
- eximius 7y ago> There's no hint in that expression that it's anything other than a method call. Compared with `fut.await`... > There's no hint in that expression that it's anything other than a field access. At least there is precedence for functions to have magical compiler-ness for 'doing stuff', instead of overloading field access which is one of the simplest operations you can have. > ... requires knowing the type of `fut` ... tbh, you don't need to know how await works. It goes and does stuff and returns a value. If you need to know what it does, you're looking up the types and seeing, 'oh its a future' and looking at that. Contrast with the field access one and you don't even know its something else entirely. I guess I just don't buy that it can't be a method. Like, formally, yea, it isn't a well-defined, safe Rust method. But make the impl fn await(self: Self) -> Self::Target { intrinsic::await(self) } Now its a method defined on the `Future` trait, it's discoverable, it's clearly magic, it's ergonomic... In my opinion, it beats the field access one in every conceivable metric. It's just frustrating, I guess.
- Thiez 7y agoIf it's a method you should be able to create a vector of futures and `.map(Future::await)` or even `.map(Future::await as FnMut(_) -> _)`. Clearly this would never work, since `await` is implement by transforming the containing function into a state machine, your suggestion would make this impossible.
- eximius 7y agoI mean, why couldn't it? let newvec = vec![fut1, fut2, fut3].map(Future::await).collect() would be equivalent to let f1 = fut1.await(); let f2 = fut2.await(); let f3 = fut3.await(); let newvec = vec![f1,f2,f3]; or vec![fut1,fut2,fut3].map(|fut| fut.await).collect()
- Thiez 7y agoI think you fundamentally misunderstand async await. `vec[fut1, fut2, fut3].map(|fut| fut.await).collect()` will not do what you might expect, although I haven't bothered to read the RFC whether it will be allowed at all. Await must have compiler support and cannot just be some function you call.
- eximius 7y agoI am certainly being sloppy, though I think fundamentally misunderstanding it is a bit of an exaggeration. I know you can't await in a non-async function so, at the very least, you'd need (IDR the syntax for async closures?) vec![fut1, fut2, fut3].map_async(async|fut| fut.await).collect() I believe that could be built. Though, now I'm getting myself tripped up. Because I claimed that this is similar to vec![fut1, fut2, fut3].map(Future::await).collect() But I notice that I expect `await` to be a blocking, non-async function. Which, feels strange in that the lambda in the first block needs to be async but the function itself doesn't. > Await must have compiler support and cannot just be some function you call. I'm not saying it doesn't need compiler support. I explicitly state that it does - I just want the magic to be a little more explicit. Moreover, I believe that most programmers think of functions as 'encapsulated code', something that does a thing. In this sense, it matches what await does better than syntax that looks like it's accessing a field. Admittedly, in the purest sense of encapsulation, this method 'escapes' that encapsulation, but I don't consider that terribly important.
- eridius 7y ago> At least there is precedence for functions to have magical compiler-ness for 'doing stuff', instead of overloading field access which is one of the simplest operations you can have. Except we don't actually have precedence for "I'm going to declare a function, but it's not actually implemented in Rust, instead it's a cue to the compiler to do other magical stuff". Lang items are in fact the opposite; they're telling the compiler "here's the implementation for an extension point you've defined". For example the "panic_impl" lang item is a function that's invoked when code panics. The closest I can think of to what you're referring to is the fact that the type identified as "owned_box" has special dereferencing behavior, but that can be modeled as the compiler providing an implementation for an un-namable trait on the owned_box item where the trait represents the new deref behavior (just as how Deref and DerefMut represent the existing deref behaviors). > tbh, you don't need to know how await works. You need to know the type in order to determine if `fut.await()` is invoking this magical semantics-altering compiler behavior or if it's just invoking a method that happens to be named `await` (given that the lang_item proposal means await is no longer a keyword). > Contrast with the field access one and you don't even know its something else entirely. Except you do, because await is a keyword. It's impossible in Rust 2018 to have a field named await, and so any expression that looks like `x.await` is guaranteed to be an invocation of the await behavior. > I guess I just don't buy that it can't be a method. It cannot be a method that is implemented as a call to an intrinsic. The await keyword requires rewriting the function that invokes it.
- Rusky 7y agoIf we're gonna have await share syntax with some other language feature, method syntax is IMO better than field syntax- it semantically blocks the function until the callee is complete, it evaluates to a temporary instead of an lvalue, it can unwind the caller (via cancelation, like a panic). You could implement that syntax and still make `await` a keyword, without doing the usual method lookup and without letting no_std libraries change the term, just like `.await`- that's an orthogonal concern.
- eridius 7y agoThis just then boils down to saying "the await keyword should have required parens after it", which is a different argument than saying "it should be a lang item".
- eximius 7y agohell, I'd support that. A function/method 'does a thing' while field access 'reads a thing'. Rust doesn't have implicit getters and setters like Python might have. It doesn't LOOK like `fut.await` should/could DO anything, but it can.
- eridius 7y agoAh, but isn't the whole point of an `async` function that it syntactically erases the notion of blocking on an operation? If you ignore the fact that it blocks the async function (and yields control back to the executor), then `foo.await` isn't really "doing" anything at all, it's just handing you back the future's output. Having said that, I'd have to assume that `fut.await` does consume the future, which normal field access doesn't do. Personally, I'm ok with the tradeoff that says "this keyword looks like field access but it consumes the receiver, but as a result we don't have these extra parens all over the place on a keyword", especially because going the other way introduces other costs (e.g. it looks like a method call and yet can't be referred to as Future::await).
- Rusky 7y ago