3 ms·
The reason to mark a function as async (in today's Rust) is that you want to `await` something within it. If a function does this, it must be async- there's no
by Rusky 5y ago
The reason to mark a function as async (in today's Rust) is that you want to `await` something within it.
If a function does this, it must be async- there's no other way to implement it. If a function does not do this, there is no reason to make it async in the first place- it will never suspend. (Things get uglier when it may or may not need to await something, e.g. if it accepts a closure parameter. Rust doesn't really handle this today, but one approach would be to make the function polymorphic over async-ness- this is similar to but not really the same as what it sounds like you're talking about.)
Async functions are for language-level cooperative multitasking, not concurrency per se. The difference between fork/join (what you imagined async to be) and what it actually is, is more than an implementation detail- it's a semantic change to how the program behaves. If you want to make something happen in the background at the call site, there are already ways to do that, they're just not spelled "async fn."
- ridiculous_fish 5y agoBut what's the difference between an async function returning int, and a function returning `impl Future<int>`? If there's no difference why do we need the async keyword? Other parts of the language are conditioned on the return type. For example the ? operator only works if the return type is Option or Result. It seems like `await` could inspect the return type in the same way.
- kibwen 5y ago> But what's the difference between an async function returning int, and a function returning `impl Future<int>`? If there's no difference why do we need the async keyword? No difference, but while the feature was being explored early users found it simpler if they didn't need to think about lifetimes on the returned future. Sometimes sugar is just sugar; same reason that `for` loops exist instead of just using `while`.
- steveklabnik 5y agoYou can't await inside of a function returning impl Future<Int>. (You can inside an async block inside one, but there you go with language-level support again.) You need the collaboration with the compiler to make it all work well. And that means keywords and language-level infrastructure. We tried no sugar, and it was rough: http://aturon.github.io/tech/2018/04/24/async-borrowing/ http://aturon.github.io/tech/2018/04/24/async-borrowing/ (And a proc macro didn't work out either)
- Rusky 5y agoThe difference between `await` and `?` is that the former completely transforms the entire function body containing it, while `?` just conditionally returns from it. (There's less of a difference to the caller of the function, where both `async` and `impl Future` give you a `Future` object.) A normal function returning `impl Future` has normal synchronous control flow, cannot ever use `await`, and then eventually returns a value whose type implements the Future trait. You can't just return an int, because ints don't implement `Future`- you have to return something that is `poll`-able. On the other hand, an `async fn` merely looks like it has normal synchronous control flow, with some `await`s sprinkled in. But when you call it, none of that code runs- instead it immediately constructs and returns a value (of anonymous type, similar to a closure) whose type implements `Future`. The code written in the body has its control flow ripped apart at `await` points, much like a CPS transform, and it goes in the `poll` method of that value's `Future` impl. It's technically possible that the language could have been designed the way you describe- if an `await` is present, that transformation happens, otherwise it doesn't. But because of the nature of the `await` operator, this is much messier than `?`. For example, when the transformation happens, you need to be able to return an `int`, and when it doesn't you must return an `impl Future`, unlike `?` where you always have to return a `Result` or `Option` regardless of your use of `?`. And you wouldn't be able to tell at a glance whether the function body runs immediately when it's called, or whether it waits for the Future to be `poll`ed. Plus, the language also has `async { .. }` blocks that do the same transformation in an expression context, so it's kind of nice to be consistent with that.