The problem that I'm trying to illustrate is Send + Async which always comes up in this context, not just Send.
> You were always sending crap across concurrent execution contexts. But the language syntactically kinda-hid it from you and you got away with not worrying about it. And now it bit you in the ass.
I disagree. Here's a more realistic example
async fn func (resource: Resource) {
let a = foo(resource).await;
let b = bar(resource).await;
baz(a, b).await
}
Assume that this isn't "crap" and the only correct way to implement this code. There are two fundamentally distinct async operations over the same resource and you can't call them in parallel.
Now say you have an async callstack that looks like this
let task = async move {
outer(inner(func(resource).await).await).await;
};
spawn(task);
That call to func() could be buried deep in the callstack instead of inner or outer or anywhere else, and very small changes to the implementation of func can cause it to create a compiler error a mile away from where the problem actually is.
I don't feel like this is inherent complexity and it has nothing to do with tokio, it has a bunch of related problems with the limitations of Rust, from the stripped-down generator design to lack of specialization for traits.
imo these limitations make async code quite fragile to write in practice, and it's kind of frustrating to be gaslit repeatedly with "no it's actually good that your code is hard to write."
> You were always sending crap across concurrent execution contexts. But the language syntactically kinda-hid it from you and you got away with not worrying about it. And now it bit you in the ass.
I disagree. Here's a more realistic example
Assume that this isn't "crap" and the only correct way to implement this code. There are two fundamentally distinct async operations over the same resource and you can't call them in parallel.Now say you have an async callstack that looks like this
That call to func() could be buried deep in the callstack instead of inner or outer or anywhere else, and very small changes to the implementation of func can cause it to create a compiler error a mile away from where the problem actually is.I don't feel like this is inherent complexity and it has nothing to do with tokio, it has a bunch of related problems with the limitations of Rust, from the stripped-down generator design to lack of specialization for traits.
imo these limitations make async code quite fragile to write in practice, and it's kind of frustrating to be gaslit repeatedly with "no it's actually good that your code is hard to write."