It doesn't automatically race. The "async" block in Kotlin is an equivalent to "spawn" in Rust, and launches everything in it as a child task. However compared to Rusts spawn Kotlin makes use of structured concurrency - which means parent tasks wait for child tasks and if you cancel a parent task the child task will get notified. So it's meaning is definitely closer to the join() based solution you documented.
The sequential but still async version in Kotlin is simply
val result = runBlocking {
doSomethingUsefulOne() + doSomethingUsefulTwo()
}
The sequential but still async version in Kotlin is simply
No awaits necessary