async-fundamentals-initiative
async-fundamentals-initiative copied to clipboard
Attributes to indicate Send/Sync bound for Future generated in Static Async Trait
Motivation
As previously discussed it's a debated issue how to express Send/Sync bounds on the generated associated types for async methods. As I understand it, this issue will be resolved in the future. I'm currently writing a library which might profit from a way to express the bound so I would appreciate a solution for the problem to be stabilized along with static-async-trait.
Details
I'm currently developing a macro library that tries to implement about the same desugaring as described in the RFC. I came up with a solution for this using attributes on the trait methods.
Example
struct Foo;
trait Bar {
#[send]
async fn wait(&self);
}
impl Bar for Foo {
async fn wait(&self) {
todo!()
}
}
// this trait can now be used with tokio::spawn
async fn spawn_trait<T: Bar + Sync + Send + 'static>(foo: T) {
let handle = tokio::spawn(async move {
foo.wait().await;
});
handle.await;
}
A method with a body that has to be Send is just annotated with #[send]
and for Sync it's just annotated with #[sync]
.
An attribute would not be that unusual. Derive default enum uses an attribute as well.
Caveats
How would this appear in rustdoc?
I would appreciate your feedback
Some additional thoughts:
I saw a proposal that suggested introducing additional, potentially complex, syntax. This aligns more with the general way how Rust deals with types, traits and especially trait constraints. Despite this being a huge implementation undertaking it wouldn't improve readability and make Rust even more complex.
In my opinion this is also a counter argument for just solving the issue by suggesting manual desugarization.
Since one can already do that as soon as GATs and impl traits are stabilized, this feature should be considered more as a quality of life improvement or syntactic sugar. As such I would consider introducing new as syntax overkill. Solving this in a fashion that resembles the macros currently used would benefit from better accessibility and readability. It would also speed up adaption once it's stabilized.
A downside of this approach is that Send/Sync-ness has to be decided on the trait. This is especially difficult for standard traits like AsyncIterator
, and we don't want to have multiple versions of each trait (like we already do for async/sync).
Thanks for the response!
I get why it was considered to introduce new syntax. However it might be useful to let the trait decide in some situations.
However I see why my proposal is unfit.