polling-async-trait
polling-async-trait is a library that creates async methods associated with polling methods on your traits. It is similar to async-trait, but where async-trait works on async methods, polling-async-trait works on poll_ methods.
Usage
The entry point to this library is the async_poll_trait attribute. When applied to a trait, it scans the trait for each method tagged with async_method. It treats each of these methods as an async polling method, and for each one, it adds an equivalent async method to the trait.
use polling_async_trait::async_poll_trait;
use std::io;
#[async_poll_trait]
trait ExampleTrait {
#[async_method]
fn poll_basic(&mut self, cx: &mut Context<'_>) -> Poll<i32>;
#[async_method]
fn poll_ref_method(&self, cx: &mut Context<'_>) -> Poll<i32>;
#[async_method]
fn poll_pin_method(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<i32>;
#[async_method(owned)]
fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
#[async_method(owned)]
fn poll_close_ref(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
#[async_method(owned)]
fn poll_close_pinned(self: Pin<&mut Self>, cx: &mut Context<'_>)
-> Poll<io::Result<()>>;
#[async_method(method_name = "do_work", future_name = "DoWork")]
fn poll_work(&mut self, cx: &mut Context<'_>) -> Poll<()>;
}
#[derive(Default)]
struct ExampleStruct {
closed: bool,
}
impl ExampleTrait for ExampleStruct {
fn poll_basic(&mut self, cx: &mut Context<'_>) -> Poll<i32> {
Poll::Ready(10)
}
fn poll_ref_method(&self, cx: &mut Context<'_>) -> Poll<i32> {
Poll::Ready(20)
}
fn poll_pin_method(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<i32> {
Poll::Ready(30)
}
fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
if !self.closed {
println!("closing...");
self.closed = true;
cx.waker().wake_by_ref();
Poll::Pending
} else {
println!("closed!");
Poll::Ready(Ok(()))
}
}
fn poll_close_ref(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
if !self.closed {
println!("Error, couldn't close...");
Poll::Ready(Err(io::ErrorKind::Other.into()))
} else {
println!("closed!");
Poll::Ready(Ok(()))
}
}
fn poll_close_pinned(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
if !this.closed {
println!("closing...");
this.closed = true;
cx.waker().wake_by_ref();
Poll::Pending
} else {
println!("closed!");
Poll::Ready(Ok(()))
}
}
fn poll_work(&mut self, cx: &mut Context<'_>) -> Poll<()> {
Poll::Ready(())
}
}
#[tokio::main]
async fn main() -> io::Result<()> {
let mut data1 = ExampleStruct::default();
assert_eq!(data1.basic().await, 10);
assert_eq!(data1.ref_method().await, 20);
data1.do_work().await;
data1.close().await?;
let data2 = ExampleStruct::default();
assert!(data2.close_ref().await.is_err());
let mut data3 = Box::pin(ExampleStruct::default());
assert_eq!(data3.as_mut().pin_method().await, 30);
let data4 = ExampleStruct::default();
data4.close_pinned().await?;
Ok(())
}
The generated future types will share visibility with the trait (that is, they will be pub if the trait is pub, pub(crate) if the trait is pub(crate), etc).
Consider carefully which library is best for your use case; polling methods are often much more difficult to write (because they require manual state management & dealing with Pin). If your control flow is complex, it's probably preferable to use an async fn and async-trait. The advantage of polling-async-trait is that the async methods it creates are 0-overhead, because the returned futures call the poll methods directly. This means there's no need to use a type-erased Box<dyn Future ... >.
License: MPL-2.0