> I don't understand the arguments on either side. In terms of what code looks like I far prefer being able to just declare green threads like golang does.
Under the hood `async` is sugar over a function such that the function returns a `Future<T>` instead of a `T`. What is done with that future is up to the caller.
In most cases this is handed off to a runtime (your choice of runtime, generally speaking) that will figure out how to execute it. You could also manually poll the future until it's complete, which does happen sometimes if you're manually implementing the Future trait.
If you have no async code you can simply avoid having an async runtime altogether, reducing the required runtime for an arbitrary program.
> I far prefer being able to just declare green threads like golang does
This relies on an implicit runtime. That's fine - lots of Rust libraries that work the way you're suggesting will just assume a runtime exists.
That lets you write:
spawn(async {println!("hello from async");});
And, just like a goroutine, it will be scheduled for execution by the implicit runtime (or it will panic if that runtime is not there).Note that this implicit runtime has to be there or you'll panic. This means that the reasonable behavior would be to always provide such a runtime, which would mean that even "sync" programs would need it. Or otherwise you'd need to somehow determine that no "async" code is ever actually called and statically remove it. That is a major reason why you wouldn't want this model in a language that tries to minimize its runtime.
> but I've been programming for 17+ years and the fact that I still don't implies to me that I never will.
I think it's just a matter of exposure. Try writing in more languages like C, C++, Rust, etc, and dig into these features.