2 stable releases
1.0.1 | Jun 28, 2019 |
---|
#1801 in Asynchronous
12KB
associate-async-io
Async IO traits that use futures instead of poll. This is an experiment to see
if using async fn
for the futures::io
traits is possible.
Why does this exist?
This is useful because currently implementing AsyncRead
, AsyncWrite
, and
Stream
require knowledge of Poll
, Pin
, and arbitrary self types. We
think it would be an ergonomics improvement if knowledge of these concepts was
not required to implement these traits. Instead once async fn
in traits comes
around we think that would make a great fit:
pub trait AsyncRead {
async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
}
pub trait AsyncWrite {
async fn write(&mut self, buf: &[u8]) -> io::Result<usize>;
}
pub trait AsyncIterator {
type Item;
async fn next(&mut self) -> Option<Self::Item>;
}
These would be direct async counterparts to Read
, Write
and Iterator
:
pub trait AsyncRead {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
}
pub trait AsyncWrite {
fn write(&mut self, buf: &[u8]) -> io::Result<usize>;
}
pub trait AsyncIterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
However currently async fn
in traits doesn't work. So we're defining these
traits with an associated type instead.
pub trait AsyncRead {
type Fut: Future<Output = io::Result<usize>>;
fn read(&mut self, buf: &mut [u8]) -> Self::Fut;
}
pub trait AsyncWrite {
type Fut: Future<Output = io::Result<usize>>;
fn write(&mut self, buf: &[u8]) -> Self::Fut;
}
pub trait AsyncIterator {
type Item;
type Fut: Future<Output = Option<Self::Item>>;
fn next(&mut self) -> Self::Fut;
}
Because of compiler reasons this means there currently is the overhead of an extra box. But we think that's fine, as it's unlikely to become a bottleneck, and this would be temporary anyway.
However a limitation is that this can't return borrowed values, as it relies on GATs. Which seems like the most convincing counterpoint to using these traits today.
Examples
#![feature(async_await)]
use futures::executor::block_on;
use associated_async_io::AsyncIterator;
use futures::future::{self, Future};
use std::pin::Pin;
#[derive(Debug)]
struct KittenIterator {
cursor: usize,
kittens: Vec<String>,
}
impl KittenIterator {
fn new(mut kittens: Vec<String>) -> Self {
kittens.reverse();
Self { cursor: 0, kittens }
}
}
impl AsyncIterator for KittenIterator {
type Item = String;
type Fut = Pin<Box<Future<Output = Option<Self::Item>>>>;
fn next(&mut self) -> Self::Fut {
self.cursor += 1;
let kitten = self.kittens.pop();
Box::pin(future::ready(kitten))
}
}
fn main () {
block_on(async {
let kittens = vec!["chashu".to_owned(), "nori".to_owned()];
let mut kittens = KittenIterator::new(kittens);
AsyncIterator::next(&mut kittens);
})
}
Installation
$ cargo add associate-async-io
Safety
This crate uses #![deny(unsafe_code)]
to ensure everything is implemented in
100% Safe Rust.
Contributing
Want to join us? Check out our "Contributing" guide and take a look at some of these issues:
References
None.
License
MIT OR Apache-2.0