Skip to content

improve readability of macros #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
language: rust
rust:
- stable
- nightly

before_script: |
rustup component add rustfmt-preview &&
rustup component add clippy-preview
rustup component add clippy-preview;
if ! rustup component add clippy; then
target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/clippy`;
echo "'clippy' is unavailable on the toolchain 'nightly', using the toolchain 'nightly-$target' instead";
rustup toolchain install nightly-$target;
rustup default nightly-$target;
rustup component add clippy;
fi
rustup component add rustfmt-preview;
if ! rustup component add rustfmt; then
target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`;
echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead";
rustup toolchain install nightly-$target;
rustup default nightly-$target;
rustup component add rustfmt;
fi
script: |
cargo fmt -- --check &&
cargo clippy -- -D clippy &&
Expand Down
16 changes: 10 additions & 6 deletions src/join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,24 @@ macro_rules! join {
$(
// Move future into a local so that it is pinned in one place and
// is no longer accessible by the end user.
let mut $fut = $crate::maybe_done($fut);
let mut $fut = $crate::MaybeDone::new($fut);
)*
$crate::utils::poll_fn(move |cx| {
use $crate::utils::future::Future;
use $crate::utils::task::Poll;
use $crate::utils::pin::Pin;

let mut all_done = true;
$(
all_done &= $crate::utils::future::Future::poll(
unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }, cx).is_ready();
let fut = unsafe { Pin::new_unchecked(&mut $fut) };
all_done &= Future::poll(fut, cx).is_ready();
)*
if all_done {
$crate::utils::task::Poll::Ready(($(
unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.take_output().unwrap(),
Poll::Ready(($(
unsafe { Pin::new_unchecked(&mut $fut) }.take().unwrap(),
)*))
} else {
$crate::utils::task::Poll::Pending
Poll::Pending
}
}).await
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ mod select;
mod try_join;
mod try_select;

pub use maybe_done::{maybe_done, MaybeDone};
pub use maybe_done::MaybeDone;

/// Helper re-exports for use in macros.
pub mod utils {
Expand Down
46 changes: 24 additions & 22 deletions src/maybe_done.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ use core::pin::Pin;
use futures_core::ready;
use futures_core::task::{Context, Poll};

/// Create a new instance of `MaybeDone`.
pub fn maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut> {
MaybeDone::Future(future)
}

/// A future that may have completed.
#[derive(Debug)]
pub enum MaybeDone<Fut: Future> {
Expand All @@ -23,29 +18,19 @@ pub enum MaybeDone<Fut: Future> {
/// The output of the completed future
Done(Fut::Output),
/// The empty variant after the result of a [`MaybeDone`] has been
/// taken using the [`take_output`](MaybeDone::take_output) method.
/// taken using the [`take`](MaybeDone::take) method.
Gone,
}

impl<Fut: Future> MaybeDone<Fut> {
/// Returns an [`Option`] containing a mutable reference to the output of the future.
/// The output of this method will be [`Some`] if and only if the inner
/// future has been completed and [`take_output`](MaybeDone::take_output)
/// has not yet been called.
#[inline]
pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> {
unsafe {
let this = self.get_unchecked_mut();
match this {
MaybeDone::Done(res) => Some(res),
_ => None,
}
}
/// Create a new instance of `MaybeDone`.
pub fn new(future: Fut) -> MaybeDone<Fut> {
Self::Future(future)
}

/// Returns an [`Option`] containing a reference to the output of the future.
/// The output of this method will be [`Some`] if and only if the inner
/// future has been completed and [`take_output`](MaybeDone::take_output)
/// future has been completed and [`take`](MaybeDone::take)
/// has not yet been called.
#[inline]
pub fn output(self: Pin<&Self>) -> Option<&Fut::Output> {
Expand All @@ -56,10 +41,25 @@ impl<Fut: Future> MaybeDone<Fut> {
}
}

/// Returns an [`Option`] containing a mutable reference to the output of the future.
/// The output of this method will be [`Some`] if and only if the inner
/// future has been completed and [`take`](MaybeDone::take)
/// has not yet been called.
#[inline]
pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> {
unsafe {
let this = self.get_unchecked_mut();
match this {
MaybeDone::Done(res) => Some(res),
_ => None,
}
}
}

/// Attempt to take the output of a `MaybeDone` without driving it
/// towards completion.
#[inline]
pub fn take_output(self: Pin<&mut Self>) -> Option<Fut::Output> {
pub fn take(self: Pin<&mut Self>) -> Option<Fut::Output> {
unsafe {
let this = self.get_unchecked_mut();
match this {
Expand All @@ -73,14 +73,16 @@ impl<Fut: Future> MaybeDone<Fut> {
}
}
}

// fn ok(self) -> Option<Fut::Output> {}
}

impl<Fut: Future> Future for MaybeDone<Fut> {
type Output = ();

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let res = unsafe {
match self.as_mut().get_unchecked_mut() {
match Pin::as_mut(&mut self).get_unchecked_mut() {
MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)),
MaybeDone::Done(_) => return Poll::Ready(()),
MaybeDone::Gone => panic!("MaybeDone polled after value taken"),
Expand Down
4 changes: 2 additions & 2 deletions src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ macro_rules! select {
$(
// Move future into a local so that it is pinned in one place and
// is no longer accessible by the end user.
let mut $fut = $crate::maybe_done($fut);
let mut $fut = $crate::MaybeDone::new($fut);
)*
$crate::utils::poll_fn(move |cx| {
use $crate::utils::future::Future;
Expand All @@ -46,7 +46,7 @@ macro_rules! select {
let fut = unsafe { Pin::new_unchecked(&mut $fut) };
if Future::poll(fut, cx).is_ready() {
let fut = unsafe { Pin::new_unchecked(&mut $fut) };
let output = fut.take_output().unwrap();
let output = fut.take().unwrap();
return Poll::Ready(output);
}
)*
Expand Down
48 changes: 29 additions & 19 deletions src/try_join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,39 +43,49 @@
macro_rules! try_join {
($($fut:ident),* $(,)?) => { {
async {
use $crate::utils::future::Future;
use $crate::utils::pin::Pin;
use $crate::utils::poll_fn;
use $crate::utils::result::Result;
use $crate::utils::task::Poll;

$(
// Move future into a local so that it is pinned in one place and
// is no longer accessible by the end user.
let mut $fut = $crate::maybe_done($fut);
let mut $fut = $crate::MaybeDone::new($fut);
)*

let res: $crate::utils::result::Result<_, _> = $crate::utils::poll_fn(move |cx| {
let res: Result<_, _> = poll_fn(move |cx| {
let mut all_done = true;
$(
if $crate::utils::future::Future::poll(
unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }, cx).is_pending()
{
let fut = unsafe { Pin::new_unchecked(&mut $fut) };
if Future::poll(fut, cx).is_pending() {
all_done = false;
} else if unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.output_mut().unwrap().is_err() {
} else if unsafe { Pin::new_unchecked(&mut $fut) }.output_mut().unwrap().is_err() {
// `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce
// a `T: Debug` bound.
return $crate::utils::task::Poll::Ready(
$crate::utils::result::Result::Err(
unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.take_output().unwrap().err().unwrap()
)
);
return Poll::Ready(
Result::Err(unsafe { Pin::new_unchecked(&mut $fut) }
.take()
.unwrap()
.err()
.unwrap()
));
}
)*
if all_done {
$crate::utils::task::Poll::Ready(
$crate::utils::result::Result::Ok(($(
// `.ok().unwrap()` rather than `.unwrap()` so that we don't introduce
// an `E: Debug` bound.
unsafe { $crate::utils::pin::Pin::new_unchecked(&mut $fut) }.take_output().unwrap().ok().unwrap(),
)*))
)
let res = ($(
// `.ok().unwrap()` rather than `.unwrap()` so that we don't introduce
// an `E: Debug` bound.
unsafe { Pin::new_unchecked(&mut $fut) }
.take()
.unwrap()
.ok()
.unwrap(),
)*);
Poll::Ready(Result::Ok(res))
} else {
$crate::utils::task::Poll::Pending
Poll::Pending
}
}).await;
res
Expand Down
29 changes: 16 additions & 13 deletions src/try_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
///
/// `try_select!` is similar to [`select!`], but keeps going if a future
/// resolved to an error until all futures have been resolved. In which case
/// the last error found will be returned.
/// the error of the last item in the list will be returned.
///
/// This macro is only usable inside of async functions, closures, and blocks.
///
Expand All @@ -19,7 +19,7 @@
/// use futures::future;
/// use std::io::{Error, ErrorKind};
///
/// let a = future::pending::<Result<u8, Error>>();
/// let a = future::pending::<Result<_, Error>>();
/// let b = future::ready(Err(Error::from(ErrorKind::Other)));
/// let c = future::ready(Ok(1u8));
///
Expand All @@ -33,16 +33,19 @@
macro_rules! try_select {
($($fut:ident),+ $(,)?) => { {
async {
use $crate::utils::future::Future;
use $crate::utils::pin::Pin;
use $crate::utils::poll_fn;
use $crate::utils::result::Result;
use $crate::utils::task::Poll;

$(
// Move future into a local so that it is pinned in one place and
// is no longer accessible by the end user.
let mut $fut = $crate::maybe_done($fut);
let mut $fut = $crate::MaybeDone::new($fut);
)*
$crate::utils::poll_fn(move |cx| {
use $crate::utils::future::Future;
use $crate::utils::task::Poll;
use $crate::utils::pin::Pin;

let res: Result<_, _> = poll_fn(move |cx| {
let mut all_done = true;

$(
Expand All @@ -51,8 +54,8 @@ macro_rules! try_select {
let fut = Pin::new(&$fut);
if fut.output().unwrap().is_ok() {
let fut = unsafe { Pin::new_unchecked(&mut $fut) };
let output = fut.take_output().unwrap();
return Poll::Ready(output);
let res = fut.take().unwrap();
return Poll::Ready(res);
} else {
all_done = false;
}
Expand All @@ -62,20 +65,20 @@ macro_rules! try_select {
)*

if all_done {
// We need to iterate over all items to not get an
// "unreachable code" warning.
// We need to iterate over all items to get the last error.
let mut err = None;
$(
if err.is_none() {
let fut = unsafe { Pin::new_unchecked(&mut $fut) };
err = Some(fut.take_output().unwrap());
err = Some(fut.take().unwrap());
}
)*
return Poll::Ready(err.unwrap());
} else {
Poll::Pending
}
}).await
}).await;
res
}
} }
}