Rust 1.75, to be released December 28, contains support for async fn
and -> impl Trait
in traits (AFIT and RPITIT, respectively).
pub trait HttpService {
// Desugars to:
// fn fetch(&self, url: Url) -> impl Future<Output = HtmlBody>
async fn fetch(&self, url: Url) -> HtmlBody;
}
However, if you try to use async fn
in a public trait, you will get a warning:
warning: use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
--> src/lib.rs:7:5
|
7 | async fn fetch(&self, url: Url) -> HtmlBody;
| ^^^^^
|
= note: you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
= note: `#[warn(async_fn_in_trait)]` on by default
help: you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
7 - async fn fetch(&self, url: Url) -> HtmlBody;
7 + fn fetch(&self, url: Url) -> impl std::future::Future<Output = HtmlBody> + Send;
|
This warning is asking you to choose whether you want your trait to be compatible with work-stealing executors. Without Send
bounds on the trait itself, you couldn't write code like this.
fn spawn_task(service: impl HttpService + 'static) {
// error: future cannot be sent between threads safely!
tokio::spawn(async move {
let url = Url::from("https://rust-lang.org");
let _body = service.fetch(url).await;
});
}
trait-variant by the Rust Async Working Group provides a convenient proc macro to provide both Send
and non-Send
variants of a trait. Add it to your project with cargo add trait-variant
.
#[trait_variant::make(HttpService: Send)]
pub trait LocalHttpService {
// In the HttpService variant, returns impl Future + Send.
async fn fetch(&self, url: Url) -> HtmlBody;
}
After replacing the original trait definition with the one above, the warning goes away and spawn_task
compiles without modification. Applications that do not use work-stealing executors can use the original LocalHttpService
trait, which allows non-Send
futures.
I would like to include this crate in the official announcement of AFIT and RPITIT later this week. Please try it out and leave feedback if you have a use for it. Thank you!