The discussion in Await Syntax Discussion Summary is centered around what that the Await Syntax Write Up calls the Error Handling Problem. In a fictive program that queries a database, one could imagine having code like the following:
let conn = (await open_db("http://user@localhost:1234"))?;
let stmt = (await prepare_statement(conn, "SELECT * FROM posts"))?;
let posts = (await stmt.execute())?;
for post in posts { /* ... */ }
It is the use of parenthesis around await
that is seen as problematic. They are required due to the use of ?
which we want to apply to the resolved value, not the Future
we await
.
One simple solution would be to move the ?
down to where the resolved value is used:
let conn = await open_db("http://user@localhost:1234");
let stmt = await prepare_statement(conn?, "SELECT * FROM posts");
let posts = await stmt?.execute();
for post in posts? { /* ... */ }
That works for cases like the above where the values are chained from line to line.
The write up says that part of the error handling problem is the extra parenthesis. It goes on to suggest that one could add a magic ,await()
or .await!()
method so that the above becomes:
let conn = open_db("http://user@localhost:1234").await()?;
let stmt = prepare_statement(conn, "SELECT * FROM posts").await()?;
let posts = stmt.execute().await()?;
for post in posts { /* ... */ }
The careful reader will of course notice that the number of parenthesis hasn’t changed compared to the first version. So it cannot really be the parenthesis as such that cause the problem. Instead, I think the problem is that await async_expr
make waiting on the asynchronous expression a bigger deal than some think it should be.
On the face of it, await
is something that takes a Future<T>
and gives you a T
. Hiding await
in a method call might seem nice at first, but I believe it’s a mistake since I believe await()
works differently than normal methods. Making it look like a regular method would knowingly introduce a corner case to an otherwise normal part of the syntax. Please don’t do that — Rust is big enough that we don’t need more corner cases.
So why am I claiming that await()
is not a normal method? In Rust, as in all other languages I know, calling a function (or invoking a method) means that the thread of execution jumps into the code of the function. A new stack frame is created, the function body executes and when it returns, you’re back to where you started. You go down the call stack when you call a function and you go up the stack when you return.
Awaiting is vastly different. When you await async_expr
, you’re suspending the current function and instead of going down the call stack, the thread of execution goes up the stack to an event loop. Instead of calling it await
, keyword should perhaps have been called return_and_please_invoke_me_later
Arbitrary things can happen when you call await
— you don’t know what code will execute next and you don’t know if the suspended function will ever be invoked again. Seeing it in this light should make it clear that it’s nothing like a method call (or attribute access). It’s a different (new!) thing.