4 ms·
The answer is actually pretty dumb: functions are declared async at the declaration site because that's how C# supported legacy code. Introducing a new keyword
by ridiculous_fish 5y ago
The answer is actually pretty dumb: functions are declared async at the declaration site because that's how C# supported legacy code.
Introducing a new keyword is a breaking change because existing code may have a variable with the same name. So `await` is a conditional keyword in C#, conditioned on whether the function is marked `async`. And other languages have since just copied that choice.
https://dotnetfiddle.net/JUE0Sy https://dotnetfiddle.net/JUE0Sy
- kibwen 5y agoThat may have been C#'s rationale, but it wasn't Rust's rationale. Rust doesn't even strictly require the `async` keyword in a signature to define functions that work with `await`; `await` works by polling a future, and `async fn` is just sugar for defining a function that returns a future. In fact Rust does precisely what the parent suggests, and also offers `async {}` blocks as sugar for defining inline futures: fn bar() -> impl Future<Output = u8> { async { let x: u8 = foo().await; x + 5 } }
- ridiculous_fish 5y agoNeat, I didn't know about the `async {}` feature. I think it's obvious that Rust chose `async fn` for familiarity with other languages. As you say `async` is just sugar on the return type, so logically it should decorate that type, not the fn keyword. "async fn" is even misleading since the function itself returns synchronously (and immediately): it's the returned value where the async-ness lies. So I think Rust's use and placement of `async` is for familiarity, which traces back to C#'s original choice. Not to say that's a bad rationale. Admittedly I didn't follow any of the Rust async design discussions so I could be completely wrong about all of it.
- Rusky 5y ago> "async fn" is even misleading since the function itself returns synchronously (and immediately): it's the returned value where the async-ness lies. You've got the causality backwards- it's the async-ness that causes it to return immediately. Async is primarily about the execution model of the entire function, and the return type is only changed in service of that model. The course of the design discussions started without async or await, using library-based Future combinators. Async was introduced as a way to desugar control flow and move knowledge of Futures into the compiler for borrow checking reasons, not out of any intention to mimic C#.
- ridiculous_fish 5y agoYou're quite correct, I hadn't thought of the fact that the transform and the return type were distinct. My mistake.