I think Go got it right by inverting the logic around async/await. In Go you have to explicitly state that a function is to run in the background via "go fn(...)". This makes it much clearer that this code will execute concurrently. In the async/await world you can't tell by looking at a function call if it will block until it's done. Forgot an await? No compile error but your program might behave in weird ways. This…
In Rust an async function is really just a const fn that synchronously only constructs and returns a state machine struct that implements the Future trait.
So
async fn foo(x: i32) { }
essentially desugars to
const fn foo(x: i32) -> FooFuture { FooFuture { x } }
struct FooFuture { x: i32 } // technically it's an enum modelling the state machine
impl Future for FooFuture { ... }
You have to explicitly spawn that onto a runtime or await it (i.e. combine it into the state machine that your code is already in). So that's actually really cool about how Rust handles async; that an async fn really isn't doing any magic, it just constructs a state machine and never interacts (or spawns) with a runtime at all, so it never starts running in the background, you are always in full control. And by throwing the future away, you are essentially cancelling it, there's no need to interact with any runtime either.