`await` vs. `yield`


#1

Reading through the “Resolve await syntax” thread and pondering various syntax options, I have begun to wonder what the difference between await and yield really is. AFAICT (and please correct me where I’m wrong!):

  • in a generator, yield saves the state of the closure and returns the yielded value, suspending the closure until it is resumed.
  • in an async function await similarly yields a future, but to a conceptual second, outer closure that handles polling the yielded future (i.e., the future being awaited) before the inner closure can be resumed.

However, it is the function being declared async that creates that second, interposing closure. But from the point of view of the closure created by the user code, it does the same as in a run-of-the-mill generator: yielding the value, which just so happens to be a Future, and waiting to be resumed.

IOW, is there an actual difference between await and yield, or couldn’t we just use the same keyword/operator/whatever the final syntax will be? In a regular closure it’d just yield the future, in an async closure that yielded future and resumption both being intercepted by the interposing code created by async.


#2

await would be pretty much equivalent to Python’s yield from, if Rust’s generators accepted arguments (and ignoring pinning and the fact that GeneratorState and Poll are different enums). Without that capability it’s closest to a manual loop:

await foo

expands to something like (again, ignoring pinning because it’s not important to understand the expansion)

{
    let mut future = foo;
    loop {
        match future.poll(magical_local_waker_variable) {
            Poll::Pending => yield Poll::Pending,
            Poll::Ready(value) => break value,
        }
    }
}

note that it is not the future that is being yielded, the current generator is polling it and then yielding Pending if the future returned it. Also see the magical_local_waker_variable, this is part of what blocks being able to nicely implement async/await as macros in a user library, somehow the two macros must communicate this variable.


#3

Thanks for the details! Also, come to think of it, there’d be the case of async streams of futures: yielding result futures vs. awaiting internal process futures (to also answer my own question).