4

This post implies that the creators of Rust had to add the "async" keyword to make the async/await functionality backward compatible.

Why do we need the async keyword?

In a new language, where backward compatibility is not an issue, would there be any drawbacks to eliminating "async"? An alternative might be to have the compiler check to see if a function uses "await" and if so, treat it as an async function.

2

1 Answer 1

5

Indeed, having an async keyword is technically unnecessary. This would also avoid issues such as the what color is your function? problem.

But it's not without cost. There might be a cost in performance, and a cost in clarity.

Async code implies a very different execution model from synchronous code. In some languages, this is closely coupled with multithreading so just eliding async would have little consequence. But in many languages, an async function is transformed by the compiler into a state machine that can be suspended and resumed. Such a state machine would have unnecessary overhead for ordinary synchronous code. Callers of such functions would also have to start to await every return value.

Your suggestion to automatically make functions async if they contain an await is feasible. Similarly, Python turns a function into a generator if it contains a yield. But changing the type and execution model of a function depending on the implementation details of the function would not make it easy to understand the code.

In practice, the async keyword as used by Rust of C# does the following. We take a function like this:

async T DoSomething() { ... }

The compiler transforms this into the equivalent definition

Task<T> DoSomething() { new StateMachine { ... } }

With your suggestion of an implicit async we wouldn't be able to tell if a declaration T DoSomething() { ... } actually has the type T DoSomething() or if it has type Task<T> DoSomething(). The everything-is-async approach would always result in the latter, but as mentioned this would have extra runtime overhead and extra awaits.

So in my opinion, sticking with an async keyword is perfectly fine. However, I would like it if future languages make it easier to abstract over async and non-async code, for example to be able to write a function that takes a callback that may or may not be async. To some degree this should be possible since the desugaring of an async function is an ordinary function that returns a Task/Promise/Future. However, I know of no language that is both designed for the efficient transformation-into-state-machine approach and makes it possible to make an await dependent on a type parameter.

1
  • 1
    Your last paragraph is important. Today - async is viral. And that makes it difficult to use. But it is necessary because it changes the type signature of methods in an extremely semantically important - and intrusive! - way. And you need to know that - it really needs to be explicit. If there was some sort of automatic coercion/casting that could happen depending on context then maybe this would work without keywords.
    – davidbak
    Commented Jul 1, 2021 at 22:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.