From 3df23c81bf3d4b7aa9028312b4f696e28e04de91 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Mon, 6 Apr 2020 19:15:06 +0100 Subject: [PATCH 1/3] Refactor to reduce the amount of unsafe and duplicated code. - Replace uses of `pin_utils` with the safer `pin_project` crate - Implement macros to allow building new combinators from existing ones more easily - Get rid of Chain as the building block for future combinators, and instead build from `Map` and `Flatten` primitives - Delete a lot of code which is no longer necessary. --- futures-util/Cargo.toml | 1 + futures-util/src/fns.rs | 354 ++++++++++++++++++ futures-util/src/future/abortable.rs | 10 +- futures-util/src/future/either.rs | 159 ++++---- .../src/future/future/catch_unwind.rs | 21 +- futures-util/src/future/future/chain.rs | 58 --- futures-util/src/future/future/flatten.rs | 167 +++++++-- .../src/future/future/flatten_stream.rs | 89 ----- futures-util/src/future/future/fuse.rs | 34 +- futures-util/src/future/future/inspect.rs | 47 --- futures-util/src/future/future/into_stream.rs | 43 --- futures-util/src/future/future/map.rs | 35 +- futures-util/src/future/future/mod.rs | 108 ++++-- futures-util/src/future/future/never_error.rs | 36 -- .../src/future/future/remote_handle.rs | 25 +- futures-util/src/future/future/then.rs | 46 --- futures-util/src/future/future/unit_error.rs | 35 -- futures-util/src/future/join.rs | 16 +- futures-util/src/future/join_all.rs | 53 +-- futures-util/src/future/maybe_done.rs | 41 +- futures-util/src/future/mod.rs | 13 +- futures-util/src/future/option.rs | 17 +- .../src/future/try_future/and_then.rs | 53 --- .../src/future/try_future/err_into.rs | 48 --- .../src/future/try_future/flatten_sink.rs | 76 ---- .../future/try_future/flatten_stream_sink.rs | 181 --------- .../src/future/try_future/inspect_err.rs | 53 --- .../src/future/try_future/inspect_ok.rs | 53 --- .../src/future/try_future/into_future.rs | 17 +- futures-util/src/future/try_future/map_err.rs | 52 --- futures-util/src/future/try_future/map_ok.rs | 54 --- .../src/future/try_future/map_ok_or_else.rs | 59 --- futures-util/src/future/try_future/mod.rs | 201 +++++++--- futures-util/src/future/try_future/or_else.rs | 56 --- .../src/future/try_future/try_chain.rs | 110 ------ .../src/future/try_future/try_flatten.rs | 180 +++++++++ .../src/future/try_future/try_flatten_err.rs | 61 +++ .../future/try_future/try_flatten_stream.rs | 91 ----- .../src/future/try_future/unwrap_or_else.rs | 55 --- futures-util/src/future/try_join.rs | 39 +- futures-util/src/future/try_join_all.rs | 59 +-- futures-util/src/future/try_maybe_done.rs | 99 +++++ futures-util/src/io/buf_reader.rs | 110 ++---- futures-util/src/io/buf_writer.rs | 91 +---- futures-util/src/io/chain.rs | 64 ++-- futures-util/src/io/copy.rs | 12 +- futures-util/src/io/copy_buf.rs | 22 +- futures-util/src/io/into_sink.rs | 36 +- futures-util/src/io/lines.rs | 11 +- futures-util/src/io/read_line.rs | 2 +- futures-util/src/io/read_to_string.rs | 2 +- futures-util/src/io/take.rs | 121 ++---- futures-util/src/lib.rs | 224 ++++++++++- futures-util/src/sink/buffer.rs | 66 ++-- futures-util/src/sink/err_into.rs | 42 +-- futures-util/src/sink/fanout.rs | 56 ++- futures-util/src/sink/map_err.rs | 53 +-- futures-util/src/sink/with.rs | 77 ++-- futures-util/src/sink/with_flat_map.rs | 71 +--- futures-util/src/stream/futures_ordered.rs | 15 +- futures-util/src/stream/once.rs | 21 +- futures-util/src/stream/select.rs | 21 +- .../src/stream/stream/buffer_unordered.rs | 60 +-- futures-util/src/stream/stream/buffered.rs | 65 +--- .../src/stream/stream/catch_unwind.rs | 21 +- futures-util/src/stream/stream/chain.rs | 19 +- futures-util/src/stream/stream/chunks.rs | 60 +-- futures-util/src/stream/stream/collect.rs | 22 +- futures-util/src/stream/stream/concat.rs | 20 +- futures-util/src/stream/stream/enumerate.rs | 55 +-- futures-util/src/stream/stream/filter.rs | 88 ++--- futures-util/src/stream/stream/filter_map.rs | 89 ++--- futures-util/src/stream/stream/flat_map.rs | 169 --------- futures-util/src/stream/stream/flatten.rs | 97 ++--- futures-util/src/stream/stream/fold.rs | 50 ++- futures-util/src/stream/stream/for_each.rs | 41 +- .../src/stream/stream/for_each_concurrent.rs | 34 +- futures-util/src/stream/stream/forward.rs | 84 ++--- futures-util/src/stream/stream/fuse.rs | 53 +-- futures-util/src/stream/stream/inspect.rs | 119 ------ futures-util/src/stream/stream/into_future.rs | 7 +- futures-util/src/stream/stream/map.rs | 81 ++-- futures-util/src/stream/stream/mod.rs | 33 +- futures-util/src/stream/stream/peek.rs | 110 ++---- .../src/stream/stream/ready_chunks.rs | 68 +--- futures-util/src/stream/stream/scan.rs | 84 ++--- futures-util/src/stream/stream/skip.rs | 57 +-- futures-util/src/stream/stream/skip_while.rs | 107 ++---- futures-util/src/stream/stream/take.rs | 55 +-- futures-util/src/stream/stream/take_until.rs | 67 +--- futures-util/src/stream/stream/take_while.rs | 93 ++--- futures-util/src/stream/stream/then.rs | 74 ++-- futures-util/src/stream/stream/zip.rs | 44 +-- .../src/stream/try_stream/and_then.rs | 74 ++-- .../src/stream/try_stream/err_into.rs | 98 ----- .../src/stream/try_stream/inspect_err.rs | 118 ------ .../src/stream/try_stream/inspect_ok.rs | 118 ------ .../src/stream/try_stream/into_stream.rs | 40 +- futures-util/src/stream/try_stream/map_err.rs | 112 ------ futures-util/src/stream/try_stream/map_ok.rs | 112 ------ futures-util/src/stream/try_stream/mod.rs | 55 ++- futures-util/src/stream/try_stream/or_else.rs | 79 ++-- .../stream/try_stream/try_buffer_unordered.rs | 59 +-- .../src/stream/try_stream/try_collect.rs | 28 +- .../src/stream/try_stream/try_concat.rs | 36 +- .../src/stream/try_stream/try_filter.rs | 81 ++-- .../src/stream/try_stream/try_filter_map.rs | 80 ++-- .../src/stream/try_stream/try_flatten.rs | 84 +---- .../src/stream/try_stream/try_fold.rs | 70 ++-- .../src/stream/try_stream/try_for_each.rs | 34 +- .../try_stream/try_for_each_concurrent.rs | 42 +-- .../src/stream/try_stream/try_skip_while.rs | 94 ++--- .../src/stream/try_stream/try_unfold.rs | 33 +- futures-util/src/stream/unfold.rs | 29 +- 114 files changed, 2629 insertions(+), 4995 deletions(-) create mode 100644 futures-util/src/fns.rs delete mode 100644 futures-util/src/future/future/chain.rs delete mode 100644 futures-util/src/future/future/flatten_stream.rs delete mode 100644 futures-util/src/future/future/inspect.rs delete mode 100644 futures-util/src/future/future/into_stream.rs delete mode 100644 futures-util/src/future/future/never_error.rs delete mode 100644 futures-util/src/future/future/then.rs delete mode 100644 futures-util/src/future/future/unit_error.rs delete mode 100644 futures-util/src/future/try_future/and_then.rs delete mode 100644 futures-util/src/future/try_future/err_into.rs delete mode 100644 futures-util/src/future/try_future/flatten_sink.rs delete mode 100644 futures-util/src/future/try_future/flatten_stream_sink.rs delete mode 100644 futures-util/src/future/try_future/inspect_err.rs delete mode 100644 futures-util/src/future/try_future/inspect_ok.rs delete mode 100644 futures-util/src/future/try_future/map_err.rs delete mode 100644 futures-util/src/future/try_future/map_ok.rs delete mode 100644 futures-util/src/future/try_future/map_ok_or_else.rs delete mode 100644 futures-util/src/future/try_future/or_else.rs delete mode 100644 futures-util/src/future/try_future/try_chain.rs create mode 100644 futures-util/src/future/try_future/try_flatten.rs create mode 100644 futures-util/src/future/try_future/try_flatten_err.rs delete mode 100644 futures-util/src/future/try_future/try_flatten_stream.rs delete mode 100644 futures-util/src/future/try_future/unwrap_or_else.rs create mode 100644 futures-util/src/future/try_maybe_done.rs delete mode 100644 futures-util/src/stream/stream/flat_map.rs delete mode 100644 futures-util/src/stream/stream/inspect.rs delete mode 100644 futures-util/src/stream/try_stream/err_into.rs delete mode 100644 futures-util/src/stream/try_stream/inspect_err.rs delete mode 100644 futures-util/src/stream/try_stream/inspect_ok.rs delete mode 100644 futures-util/src/stream/try_stream/map_err.rs delete mode 100644 futures-util/src/stream/try_stream/map_ok.rs diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml index 1ea067e378..11f77c460d 100644 --- a/futures-util/Cargo.toml +++ b/futures-util/Cargo.toml @@ -46,6 +46,7 @@ memchr = { version = "2.2", optional = true } futures_01 = { version = "0.1.25", optional = true, package = "futures" } tokio-io = { version = "0.1.9", optional = true } pin-utils = "0.1.0-alpha.4" +pin-project = "0.4.8" [dev-dependencies] futures = { path = "../futures", version = "0.3.4", features = ["async-await", "thread-pool"] } diff --git a/futures-util/src/fns.rs b/futures-util/src/fns.rs new file mode 100644 index 0000000000..2b4e3c6e56 --- /dev/null +++ b/futures-util/src/fns.rs @@ -0,0 +1,354 @@ +use core::marker::PhantomData; +use core::fmt::{self, Debug}; + +pub trait FnOnce1 { + type Output; + fn call_once(self, arg: A) -> Self::Output; +} + +impl FnOnce1 for T +where + T: FnOnce(A) -> R +{ + type Output = R; + fn call_once(self, arg: A) -> R { + self(arg) + } +} + +pub trait FnMut1: FnOnce1 { + fn call_mut(&mut self, arg: A) -> Self::Output; +} + +impl FnMut1 for T +where + T: FnMut(A) -> R +{ + fn call_mut(&mut self, arg: A) -> R { + self(arg) + } +} + +// Not used, but present for completeness +#[allow(unreachable_pub)] +pub trait Fn1: FnMut1 { + fn call(&self, arg: A) -> Self::Output; +} + +impl Fn1 for T +where + T: Fn(A) -> R +{ + fn call(&self, arg: A) -> R { + self(arg) + } +} + +macro_rules! trivial_fn_impls { + ($name:ident <$($arg:ident),*> $t:ty = $debug:literal) => { + impl<$($arg),*> Copy for $t {} + impl<$($arg),*> Clone for $t { + fn clone(&self) -> Self { *self } + } + impl<$($arg),*> Debug for $t { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str($debug) + } + } + impl<$($arg,)* A> FnMut1 for $t where Self: FnOnce1 { + fn call_mut(&mut self, arg: A) -> Self::Output { + self.call_once(arg) + } + } + impl<$($arg,)* A> Fn1 for $t where Self: FnOnce1 { + fn call(&self, arg: A) -> Self::Output { + self.call_once(arg) + } + } + pub(crate) fn $name<$($arg),*>() -> $t { + Default::default() + } + } +} + +pub struct OkFn(PhantomData); + +impl Default for OkFn { + fn default() -> Self { + OkFn(PhantomData) + } +} + +impl FnOnce1 for OkFn { + type Output = Result; + fn call_once(self, arg: A) -> Self::Output { + Ok(arg) + } +} + +trivial_fn_impls!(ok_fn OkFn = "Ok"); + +#[derive(Debug, Copy, Clone, Default)] +pub struct ChainFn(F, G); + +impl FnOnce1 for ChainFn +where + F: FnOnce1, + G: FnOnce1, +{ + type Output = G::Output; + fn call_once(self, arg: A) -> Self::Output { + self.1.call_once(self.0.call_once(arg)) + } +} +impl FnMut1 for ChainFn +where + F: FnMut1, + G: FnMut1, +{ + fn call_mut(&mut self, arg: A) -> Self::Output { + self.1.call_mut(self.0.call_mut(arg)) + } +} +impl Fn1 for ChainFn +where + F: Fn1, + G: Fn1, +{ + fn call(&self, arg: A) -> Self::Output { + self.1.call(self.0.call(arg)) + } +} +pub(crate) fn chain_fn(f: F, g: G) -> ChainFn { + ChainFn(f, g) +} + +#[derive(Default)] +pub struct MergeResultFn; + +impl FnOnce1> for MergeResultFn { + type Output = T; + fn call_once(self, arg: Result) -> Self::Output { + match arg { + Ok(x) => x, + Err(x) => x, + } + } +} +trivial_fn_impls!(merge_result_fn <> MergeResultFn = "merge_result"); + +#[derive(Debug, Copy, Clone, Default)] +pub struct InspectFn(F); + +impl FnOnce1 for InspectFn +where + F: for<'a> FnOnce1<&'a A, Output=()>, +{ + type Output = A; + fn call_once(self, arg: A) -> Self::Output { + self.0.call_once(&arg); + arg + } +} +impl FnMut1 for InspectFn +where + F: for<'a> FnMut1<&'a A, Output=()>, +{ + fn call_mut(&mut self, arg: A) -> Self::Output { + self.0.call_mut(&arg); + arg + } +} +impl Fn1 for InspectFn +where + F: for<'a> Fn1<&'a A, Output=()>, +{ + fn call(&self, arg: A) -> Self::Output { + self.0.call(&arg); + arg + } +} +pub(crate) fn inspect_fn(f: F) -> InspectFn { + InspectFn(f) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct MapOkFn(F); + +impl FnOnce1> for MapOkFn +where + F: FnOnce1, +{ + type Output = Result; + fn call_once(self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call_once(x)) + } +} +impl FnMut1> for MapOkFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call_mut(x)) + } +} +impl Fn1> for MapOkFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.map(|x| self.0.call(x)) + } +} +pub(crate) fn map_ok_fn(f: F) -> MapOkFn { + MapOkFn(f) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct MapErrFn(F); + +impl FnOnce1> for MapErrFn +where + F: FnOnce1, +{ + type Output = Result; + fn call_once(self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call_once(x)) + } +} +impl FnMut1> for MapErrFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call_mut(x)) + } +} +impl Fn1> for MapErrFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.map_err(|x| self.0.call(x)) + } +} +pub(crate) fn map_err_fn(f: F) -> MapErrFn { + MapErrFn(f) +} + +#[derive(Debug, Copy, Clone)] +pub struct InspectOkFn(F); + +impl<'a, F, T, E> FnOnce1<&'a Result> for InspectOkFn +where + F: FnOnce1<&'a T, Output=()> +{ + type Output = (); + fn call_once(self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { self.0.call_once(x) } + } +} +impl<'a, F, T, E> FnMut1<&'a Result> for InspectOkFn +where + F: FnMut1<&'a T, Output=()>, +{ + fn call_mut(&mut self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { self.0.call_mut(x) } + } +} +impl<'a, F, T, E> Fn1<&'a Result> for InspectOkFn +where + F: Fn1<&'a T, Output=()>, +{ + fn call(&self, arg: &'a Result) -> Self::Output { + if let Ok(x) = arg { self.0.call(x) } + } +} +pub(crate) fn inspect_ok_fn(f: F) -> InspectOkFn { + InspectOkFn(f) +} + +#[derive(Debug, Copy, Clone)] +pub struct InspectErrFn(F); + +impl<'a, F, T, E> FnOnce1<&'a Result> for InspectErrFn +where + F: FnOnce1<&'a E, Output=()> +{ + type Output = (); + fn call_once(self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { self.0.call_once(x) } + } +} +impl<'a, F, T, E> FnMut1<&'a Result> for InspectErrFn +where + F: FnMut1<&'a E, Output=()>, +{ + fn call_mut(&mut self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { self.0.call_mut(x) } + } +} +impl<'a, F, T, E> Fn1<&'a Result> for InspectErrFn +where + F: Fn1<&'a E, Output=()>, +{ + fn call(&self, arg: &'a Result) -> Self::Output { + if let Err(x) = arg { self.0.call(x) } + } +} +pub(crate) fn inspect_err_fn(f: F) -> InspectErrFn { + InspectErrFn(f) +} + +pub(crate) type MapOkOrElseFn = ChainFn, ChainFn, MergeResultFn>>; +pub(crate) fn map_ok_or_else_fn(f: F, g: G) -> MapOkOrElseFn { + chain_fn(map_ok_fn(f), chain_fn(map_err_fn(g), merge_result_fn())) +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct UnwrapOrElseFn(F); + +impl FnOnce1> for UnwrapOrElseFn +where + F: FnOnce1, +{ + type Output = T; + fn call_once(self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call_once(x)) + } +} +impl FnMut1> for UnwrapOrElseFn +where + F: FnMut1, +{ + fn call_mut(&mut self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call_mut(x)) + } +} +impl Fn1> for UnwrapOrElseFn +where + F: Fn1, +{ + fn call(&self, arg: Result) -> Self::Output { + arg.unwrap_or_else(|x| self.0.call(x)) + } +} +pub(crate) fn unwrap_or_else_fn(f: F) -> UnwrapOrElseFn { + UnwrapOrElseFn(f) +} + +pub struct IntoFn(PhantomData T>); + +impl Default for IntoFn { + fn default() -> Self { + IntoFn(PhantomData) + } +} +impl FnOnce1 for IntoFn where A: Into { + type Output = T; + fn call_once(self, arg: A) -> Self::Output { + arg.into() + } +} + +trivial_fn_impls!(into_fn IntoFn = "Into::into"); diff --git a/futures-util/src/future/abortable.rs b/futures-util/src/future/abortable.rs index 281cf6b481..3a6b587091 100644 --- a/futures-util/src/future/abortable.rs +++ b/futures-util/src/future/abortable.rs @@ -1,25 +1,23 @@ use crate::task::AtomicWaker; use futures_core::future::Future; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; use core::fmt; use core::pin::Pin; use core::sync::atomic::{AtomicBool, Ordering}; use alloc::sync::Arc; +use pin_project::pin_project; /// A future which can be remotely short-circuited using an `AbortHandle`. +#[pin_project] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Abortable { + #[pin] future: Fut, inner: Arc, } -impl Unpin for Abortable {} - impl Abortable where Fut: Future { - unsafe_pinned!(future: Fut); - /// Creates a new `Abortable` future using an existing `AbortRegistration`. /// `AbortRegistration`s can be acquired through `AbortHandle::new`. /// @@ -144,7 +142,7 @@ impl Future for Abortable where Fut: Future { } // attempt to complete the future - if let Poll::Ready(x) = self.as_mut().future().poll(cx) { + if let Poll::Ready(x) = self.as_mut().project().future.poll(cx) { return Poll::Ready(Ok(x)) } diff --git a/futures-util/src/future/either.rs b/futures-util/src/future/either.rs index 24fbbe79d8..be2882943a 100644 --- a/futures-util/src/future/either.rs +++ b/futures-util/src/future/either.rs @@ -4,15 +4,17 @@ use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, Stream}; #[cfg(feature = "sink")] use futures_sink::Sink; +use pin_project::{pin_project, project}; /// Combines two different futures, streams, or sinks having the same associated types into a single /// type. +#[pin_project] #[derive(Debug, Clone)] pub enum Either { /// First branch of the type - Left(A), + Left(#[pin] A), /// Second branch of the type - Right(B), + Right(#[pin] B), } impl Either<(T, A), (T, B)> { @@ -56,12 +58,12 @@ where { type Output = A::Output; + #[project] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll(cx), - Either::Right(x) => Pin::new_unchecked(x).poll(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll(cx), + Either::Right(x) => x.poll(cx), } } } @@ -86,12 +88,12 @@ where { type Item = A::Item; + #[project] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_next(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_next(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_next(cx), + Either::Right(x) => x.poll_next(cx), } } } @@ -117,39 +119,39 @@ where { type Error = A::Error; + #[project] fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_ready(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_ready(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_ready(cx), + Either::Right(x) => x.poll_ready(cx), } } + #[project] fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).start_send(item), - Either::Right(x) => Pin::new_unchecked(x).start_send(item), - } + #[project] + match self.project() { + Either::Left(x) => x.start_send(item), + Either::Right(x) => x.start_send(item), } } + #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_flush(cx), + Either::Right(x) => x.poll_flush(cx), } } + #[project] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_close(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_close(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_close(cx), + Either::Right(x) => x.poll_close(cx), } } } @@ -157,7 +159,8 @@ where #[cfg(feature = "io")] #[cfg(feature = "std")] mod if_std { - use super::Either; + use super::*; + use core::pin::Pin; use core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] @@ -179,29 +182,29 @@ mod if_std { } } + #[project] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_read(cx, buf), - Either::Right(x) => Pin::new_unchecked(x).poll_read(cx, buf), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_read(cx, buf), + Either::Right(x) => x.poll_read(cx, buf), } } + #[project] fn poll_read_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs), - Either::Right(x) => Pin::new_unchecked(x).poll_read_vectored(cx, bufs), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_read_vectored(cx, bufs), + Either::Right(x) => x.poll_read_vectored(cx, bufs), } } } @@ -211,47 +214,47 @@ mod if_std { A: AsyncWrite, B: AsyncWrite, { + #[project] fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_write(cx, buf), - Either::Right(x) => Pin::new_unchecked(x).poll_write(cx, buf), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_write(cx, buf), + Either::Right(x) => x.poll_write(cx, buf), } } + #[project] fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs), - Either::Right(x) => Pin::new_unchecked(x).poll_write_vectored(cx, bufs), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_write_vectored(cx, bufs), + Either::Right(x) => x.poll_write_vectored(cx, bufs), } } + #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_flush(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_flush(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_flush(cx), + Either::Right(x) => x.poll_flush(cx), } } + #[project] fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_close(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_close(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_close(cx), + Either::Right(x) => x.poll_close(cx), } } } @@ -261,16 +264,16 @@ mod if_std { A: AsyncSeek, B: AsyncSeek, { + #[project] fn poll_seek( self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_seek(cx, pos), - Either::Right(x) => Pin::new_unchecked(x).poll_seek(cx, pos), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_seek(cx, pos), + Either::Right(x) => x.poll_seek(cx, pos), } } } @@ -280,24 +283,24 @@ mod if_std { A: AsyncBufRead, B: AsyncBufRead, { + #[project] fn poll_fill_buf( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).poll_fill_buf(cx), - Either::Right(x) => Pin::new_unchecked(x).poll_fill_buf(cx), - } + #[project] + match self.project() { + Either::Left(x) => x.poll_fill_buf(cx), + Either::Right(x) => x.poll_fill_buf(cx), } } + #[project] fn consume(self: Pin<&mut Self>, amt: usize) { - unsafe { - match self.get_unchecked_mut() { - Either::Left(x) => Pin::new_unchecked(x).consume(amt), - Either::Right(x) => Pin::new_unchecked(x).consume(amt), - } + #[project] + match self.project() { + Either::Left(x) => x.consume(amt), + Either::Right(x) => x.consume(amt), } } } diff --git a/futures-util/src/future/future/catch_unwind.rs b/futures-util/src/future/future/catch_unwind.rs index e88cce7e9d..33839f681a 100644 --- a/futures-util/src/future/future/catch_unwind.rs +++ b/futures-util/src/future/future/catch_unwind.rs @@ -1,22 +1,20 @@ +use core::any::Any; +use core::pin::Pin; +use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; + use futures_core::future::Future; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; -use std::any::Any; -use std::pin::Pin; -use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; +use pin_project::pin_project; /// Future for the [`catch_unwind`](super::FutureExt::catch_unwind) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct CatchUnwind { - future: Fut, -} +pub struct CatchUnwind(#[pin] Fut); impl CatchUnwind where Fut: Future + UnwindSafe { - unsafe_pinned!(future: Fut); - pub(super) fn new(future: Fut) -> CatchUnwind { - CatchUnwind { future } + CatchUnwind(future) } } @@ -26,6 +24,7 @@ impl Future for CatchUnwind type Output = Result>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + let f = self.project().0; + catch_unwind(AssertUnwindSafe(|| f.poll(cx)))?.map(Ok) } } diff --git a/futures-util/src/future/future/chain.rs b/futures-util/src/future/future/chain.rs deleted file mode 100644 index 3f248e80fe..0000000000 --- a/futures-util/src/future/future/chain.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::task::{Context, Poll}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub(crate) enum Chain { - First(Fut1, Option), - Second(Fut2), - Empty, -} - -impl Unpin for Chain {} - -impl Chain { - pub(crate)fn is_terminated(&self) -> bool { - if let Chain::Empty = *self { true } else { false } - } -} - -impl Chain - where Fut1: Future, - Fut2: Future, -{ - pub(crate) fn new(fut1: Fut1, data: Data) -> Chain { - Chain::First(fut1, Some(data)) - } - - pub(crate) fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - f: F, - ) -> Poll - where F: FnOnce(Fut1::Output, Data) -> Fut2, - { - let mut f = Some(f); - - // Safe to call `get_unchecked_mut` because we won't move the futures. - let this = unsafe { self.get_unchecked_mut() }; - - loop { - let (output, data) = match this { - Chain::First(fut1, data) => { - let output = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)); - (output, data.take().unwrap()) - } - Chain::Second(fut2) => { - return unsafe { Pin::new_unchecked(fut2) }.poll(cx); - } - Chain::Empty => unreachable!() - }; - - *this = Chain::Empty; // Drop fut1 - let fut2 = (f.take().unwrap())(output, data); - *this = Chain::Second(fut2) - } - } -} diff --git a/futures-util/src/future/future/flatten.rs b/futures-util/src/future/future/flatten.rs index 16b3a19de9..5e2ec686ea 100644 --- a/futures-util/src/future/future/flatten.rs +++ b/futures-util/src/future/future/flatten.rs @@ -1,56 +1,165 @@ -use super::chain::Chain; -use core::fmt; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; +use futures_core::stream::{FusedStream, Stream}; +#[cfg(feature = "sink")] +use futures_sink::Sink; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; -/// Future for the [`flatten`](super::FutureExt::flatten) method. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Flatten +#[pin_project] +#[derive(Debug)] +pub enum Flatten { + First(#[pin] Fut1), + Second(#[pin] Fut2), + Empty, +} + +impl Flatten { + pub(crate) fn new(future: Fut1) -> Self { + Flatten::First(future) + } +} + +impl FusedFuture for Flatten where Fut: Future, + Fut::Output: Future, { - state: Chain, + fn is_terminated(&self) -> bool { + match self { + Flatten::Empty => true, + _ => false, + } + } } -impl Flatten +impl Future for Flatten where Fut: Future, Fut::Output: Future, { - unsafe_pinned!(state: Chain); + type Output = ::Output; - pub(super) fn new(future: Fut) -> Flatten { - Flatten { - state: Chain::new(future, ()), - } + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + Flatten::First(f) => { + let f = ready!(f.poll(cx)); + self.set(Flatten::Second(f)); + }, + Flatten::Second(f) => { + let output = ready!(f.poll(cx)); + self.set(Flatten::Empty); + break output; + }, + Flatten::Empty => return Poll::Pending, + } + }) } } -impl fmt::Debug for Flatten - where Fut: Future + fmt::Debug, - Fut::Output: fmt::Debug, +impl FusedStream for Flatten + where Fut: Future, + Fut::Output: Stream, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Flatten") - .field("state", &self.state) - .finish() + fn is_terminated(&self) -> bool { + match self { + Flatten::Empty => true, + _ => false, + } } } -impl FusedFuture for Flatten +impl Stream for Flatten where Fut: Future, - Fut::Output: Future, + Fut::Output: Stream, { - fn is_terminated(&self) -> bool { self.state.is_terminated() } + type Item = ::Item; + + #[project] + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + Flatten::First(f) => { + let f = ready!(f.poll(cx)); + self.set(Flatten::Second(f)); + }, + Flatten::Second(f) => { + let output = ready!(f.poll_next(cx)); + if output.is_none() { + self.set(Flatten::Empty); + } + break output; + }, + Flatten::Empty => break None, + } + }) + } } -impl Future for Flatten - where Fut: Future, - Fut::Output: Future, + +#[cfg(feature = "sink")] +impl Sink for Flatten +where + Fut: Future, + Fut::Output: Sink, { - type Output = ::Output; + type Error = >::Error; + + #[project] + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + Flatten::First(f) => { + let f = ready!(f.poll(cx)); + self.set(Flatten::Second(f)); + }, + Flatten::Second(f) => { + break ready!(f.poll_ready(cx)); + }, + Flatten::Empty => panic!("poll_ready called after eof"), + } + }) + } - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.state().poll(cx, |a, ()| a) + #[project] + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + #[project] + match self.project() { + Flatten::First(_) => panic!("poll_ready not called first"), + Flatten::Second(f) => f.start_send(item), + Flatten::Empty => panic!("start_send called after eof"), + } + } + + #[project] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + match self.project() { + Flatten::First(_) => Poll::Ready(Ok(())), + Flatten::Second(f) => f.poll_flush(cx), + Flatten::Empty => panic!("poll_flush called after eof"), + } + } + + #[project] + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + #[project] + let res = match self.as_mut().project() { + Flatten::Second(f) => f.poll_close(cx), + _ => Poll::Ready(Ok(())), + }; + if res.is_ready() { + self.set(Flatten::Empty); + } + res } } diff --git a/futures-util/src/future/future/flatten_stream.rs b/futures-util/src/future/future/flatten_stream.rs deleted file mode 100644 index d1108866ca..0000000000 --- a/futures-util/src/future/future/flatten_stream.rs +++ /dev/null @@ -1,89 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Stream for the [`flatten_stream`](super::FutureExt::flatten_stream) method. -#[must_use = "streams do nothing unless polled"] -pub struct FlattenStream { - state: State, -} - -impl FlattenStream { - unsafe_pinned!(state: State); - - pub(super) fn new(future: Fut) -> FlattenStream { - FlattenStream { - state: State::Future(future) - } - } -} - -impl fmt::Debug for FlattenStream - where Fut: Future + fmt::Debug, - Fut::Output: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenStream") - .field("state", &self.state) - .finish() - } -} - -#[derive(Debug)] -enum State { - // future is not yet called or called and not ready - Future(Fut), - // future resolved to Stream - Stream(St), -} - -impl State { - fn get_pin_mut(self: Pin<&mut Self>) -> State, Pin<&mut St>> { - // safety: data is never moved via the resulting &mut reference - match unsafe { self.get_unchecked_mut() } { - // safety: the future we're re-pinning here will never be moved; - // it will just be polled, then dropped in place - State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }), - // safety: the stream we're repinning here will never be moved; - // it will just be polled, then dropped in place - State::Stream(s) => State::Stream(unsafe { Pin::new_unchecked(s) }), - } - } -} - -impl FusedStream for FlattenStream - where Fut: Future, - Fut::Output: Stream + FusedStream, -{ - fn is_terminated(&self) -> bool { - match &self.state { - State::Future(_) => false, - State::Stream(stream) => stream.is_terminated(), - } - } -} - -impl Stream for FlattenStream - where Fut: Future, - Fut::Output: Stream, -{ - type Item = ::Item; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match self.as_mut().state().get_pin_mut() { - State::Future(f) => { - let stream = ready!(f.poll(cx)); - // Future resolved to stream. - // We do not return, but poll that - // stream in the next loop iteration. - self.as_mut().state().set(State::Stream(stream)); - } - State::Stream(s) => return s.poll_next(cx), - } - } - } -} diff --git a/futures-util/src/future/future/fuse.rs b/futures-util/src/future/future/fuse.rs index b5ef913034..69a8a6916a 100644 --- a/futures-util/src/future/future/fuse.rs +++ b/futures-util/src/future/future/fuse.rs @@ -1,24 +1,21 @@ use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Future for the [`fuse`](super::FutureExt::fuse) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Fuse { - future: Option, -} - -impl Fuse { - unsafe_pinned!(future: Option); +pub struct Fuse(#[pin] Option); +impl Fuse { pub(super) fn new(f: Fut) -> Fuse { - Fuse { - future: Some(f), - } + Fuse(Some(f)) } +} +impl Fuse { /// Creates a new `Fuse`-wrapped future which is already terminated. /// /// This can be useful in combination with looping and the `select!` @@ -65,13 +62,13 @@ impl Fuse { /// # }); /// ``` pub fn terminated() -> Fuse { - Fuse { future: None } + Fuse(None) } } impl FusedFuture for Fuse { fn is_terminated(&self) -> bool { - self.future.is_none() + self.0.is_none() } } @@ -79,12 +76,13 @@ impl Future for Fuse { type Output = Fut::Output; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let v = match self.as_mut().future().as_pin_mut() { - Some(fut) => ready!(fut.poll(cx)), + Poll::Ready(match self.as_mut().project().0.as_pin_mut() { + Some(fut) => { + let output = ready!(fut.poll(cx)); + self.project().0.set(None); + output + }, None => return Poll::Pending, - }; - - self.as_mut().future().set(None); - Poll::Ready(v) + }) } } diff --git a/futures-util/src/future/future/inspect.rs b/futures-util/src/future/future/inspect.rs deleted file mode 100644 index d67455aa6d..0000000000 --- a/futures-util/src/future/future/inspect.rs +++ /dev/null @@ -1,47 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect`](super::FutureExt::inspect) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Inspect { - future: Fut, - f: Option, -} - -impl Inspect { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Inspect { - Inspect { - future, - f: Some(f), - } - } -} - -impl Unpin for Inspect {} - -impl FusedFuture for Inspect - where Fut: FusedFuture, - F: FnOnce(&Fut::Output), -{ - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for Inspect - where Fut: Future, - F: FnOnce(&Fut::Output), -{ - type Output = Fut::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().poll(cx)); - let f = self.as_mut().f().take().expect("cannot poll Inspect twice"); - f(&e); - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/future/into_stream.rs b/futures-util/src/future/future/into_stream.rs deleted file mode 100644 index 616c4cbb57..0000000000 --- a/futures-util/src/future/future/into_stream.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::stream::{self, Once}; -use core::pin::Pin; -use futures_core::future::Future; -use futures_core::stream::{Stream, FusedStream}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Stream for the [`into_stream`](super::FutureExt::into_stream) method. -#[must_use = "streams do nothing unless polled"] -#[derive(Debug)] -pub struct IntoStream { - inner: Once -} - -impl IntoStream { - unsafe_pinned!(inner: Once); - - pub(super) fn new(future: Fut) -> IntoStream { - IntoStream { - inner: stream::once(future) - } - } -} - -impl Stream for IntoStream { - type Item = Fut::Output; - - #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -impl FusedStream for IntoStream { - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index b5fbfb1384..046da2b77c 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,49 +1,48 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; + +use crate::fns::FnOnce1; /// Future for the [`map`](super::FutureExt::map) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Map { + #[pin] future: Fut, f: Option, } impl Map { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - /// Creates a new Map. - pub(super) fn new(future: Fut, f: F) -> Map { + pub(crate) fn new(future: Fut, f: F) -> Map { Map { future, f: Some(f) } } } -impl Unpin for Map {} - impl FusedFuture for Map where Fut: Future, - F: FnOnce(Fut::Output) -> T, + F: FnOnce1, { fn is_terminated(&self) -> bool { self.f.is_none() } } impl Future for Map where Fut: Future, - F: FnOnce(Fut::Output) -> T, + F: FnOnce1, { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.as_mut() - .future() - .poll(cx) - .map(|output| { - let f = self.f().take() - .expect("Map must not be polled after it returned `Poll::Ready`"); - f(output) - }) + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let Map { future, f } = self.project(); + let output = ready!(future.poll(cx)); + let f = f.take() + .expect("Map must not be polled after it returned `Poll::Ready`"); + + Poll::Ready(f.call_once(output)) } } diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index e58cafc8c0..b87dae8464 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -7,6 +7,7 @@ use super::{assert_future, Either}; #[cfg(feature = "alloc")] use alloc::boxed::Box; use core::pin::Pin; + #[cfg(feature = "alloc")] use futures_core::future::{BoxFuture, LocalBoxFuture}; use futures_core::{ @@ -14,44 +15,77 @@ use futures_core::{ stream::Stream, task::{Context, Poll}, }; +use crate::never::Never; +use crate::fns::{OkFn, ok_fn, IntoFn, into_fn, InspectFn, inspect_fn}; // Combinators mod flatten; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten::Flatten; - -mod flatten_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten_stream::FlattenStream; - mod fuse; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::fuse::Fuse; - -mod into_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::into_stream::IntoStream; - mod map; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map::Map; - -mod then; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::then::Then; -mod inspect; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect::Inspect; +delegate_all!( + /// Future for the [`flatten`](super::FutureExt::flatten) method. + Flatten( + flatten::Flatten::Output> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); + +delegate_all!( + /// Stream for the [`flatten_stream`](FutureExt::flatten_stream) method. + FlattenStream( + flatten::Flatten::Output> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + where F: Future +); -mod unit_error; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unit_error::UnitError; - -mod never_error; +pub use fuse::Fuse; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::never_error::NeverError; +pub use map::Map; + +delegate_all!( + /// Stream for the [`into_stream`](FutureExt::into_stream) method. + IntoStream( + crate::stream::Once + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] +); + +delegate_all!( + /// Future for the [`map_into`](FutureExt::map_into) combinator. + MapInto( + Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`then`](FutureExt::then) method. + Then( + flatten::Flatten, Fut2> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] +); + +delegate_all!( + /// Future for the [`inspect`](FutureExt::inspect) method. + Inspect( + map::Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] +); + +delegate_all!( + /// Future for the [`never_error`](super::FutureExt::never_error) combinator. + NeverError( + Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] +); + +delegate_all!( + /// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. + UnitError( + Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] +); #[cfg(feature = "std")] mod catch_unwind; @@ -73,11 +107,6 @@ mod shared; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::shared::Shared; -// Implementation details - -mod chain; -pub(crate) use self::chain::Chain; - impl FutureExt for T where T: Future {} /// An extension trait for `Future`s that provides a variety of convenient @@ -113,6 +142,19 @@ pub trait FutureExt: Future { assert_future::(Map::new(self, f)) } + /// Map this future's output to a different type, returning a new future of + /// the resulting type. + /// + /// This function is equivalent to calling `map(Into::into)` but allows naming + /// the return type. + fn map_into(self) -> MapInto + where + Self::Output: Into, + Self: Sized, + { + assert_future::(MapInto::new(self)) + } + /// Chain on a computation for when a future finished, passing the result of /// the future to the provided closure `f`. /// diff --git a/futures-util/src/future/future/never_error.rs b/futures-util/src/future/future/never_error.rs deleted file mode 100644 index 5a68e6f952..0000000000 --- a/futures-util/src/future/future/never_error.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::never::Never; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{self, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`never_error`](super::FutureExt::never_error) combinator. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct NeverError { - future: Fut, -} - -impl NeverError { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> NeverError { - NeverError { future } - } -} - -impl Unpin for NeverError {} - -impl FusedFuture for NeverError { - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for NeverError - where Fut: Future, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - self.future().poll(cx).map(Ok) - } -} diff --git a/futures-util/src/future/future/remote_handle.rs b/futures-util/src/future/future/remote_handle.rs index ad7bff4f15..9495bec70e 100644 --- a/futures-util/src/future/future/remote_handle.rs +++ b/futures-util/src/future/future/remote_handle.rs @@ -5,7 +5,6 @@ use { future::Future, task::{Context, Poll}, }, - pin_utils::{unsafe_pinned, unsafe_unpinned}, std::{ any::Any, fmt, @@ -17,6 +16,7 @@ use { }, thread, }, + pin_project::{pin_project, project}, }; /// The handle to a remote future returned by @@ -70,10 +70,12 @@ type SendMsg = Result<::Output, Box<(dyn Any + Send + 'stati /// A future which sends its output to the corresponding `RemoteHandle`. /// Created by [`remote_handle`](crate::future::FutureExt::remote_handle). +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Remote { tx: Option>>, keep_running: Arc, + #[pin] future: CatchUnwind>, } @@ -85,29 +87,26 @@ impl fmt::Debug for Remote { } } -impl Unpin for Remote {} - -impl Remote { - unsafe_pinned!(future: CatchUnwind>); - unsafe_unpinned!(tx: Option>>); -} - impl Future for Remote { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if let Poll::Ready(_) = self.as_mut().tx().as_mut().unwrap().poll_canceled(cx) { - if !self.keep_running.load(Ordering::SeqCst) { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + let Remote { tx, keep_running, future } = self.project(); + + if let Poll::Ready(_) = tx.as_mut().unwrap().poll_canceled(cx) { + if !keep_running.load(Ordering::SeqCst) { // Cancelled, bail out return Poll::Ready(()) } } - let output = ready!(self.as_mut().future().poll(cx)); + let output = ready!(future.poll(cx)); // if the receiving end has gone away then that's ok, we just ignore the // send error here. - drop(self.as_mut().tx().take().unwrap().send(output)); + drop(tx.take().unwrap().send(output)); Poll::Ready(()) } } diff --git a/futures-util/src/future/future/then.rs b/futures-util/src/future/future/then.rs deleted file mode 100644 index 9f30f09864..0000000000 --- a/futures-util/src/future/future/then.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::Chain; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`then`](super::FutureExt::then) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Then { - chain: Chain, -} - -impl Then - where Fut1: Future, - Fut2: Future, -{ - unsafe_pinned!(chain: Chain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> Then { - Then { - chain: Chain::new(future, f), - } - } -} - -impl FusedFuture for Then - where Fut1: Future, - Fut2: Future, - F: FnOnce(Fut1::Output) -> Fut2, -{ - fn is_terminated(&self) -> bool { self.chain.is_terminated() } -} - -impl Future for Then - where Fut1: Future, - Fut2: Future, - F: FnOnce(Fut1::Output) -> Fut2, -{ - type Output = Fut2::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.as_mut().chain().poll(cx, |output, f| f(output)) - } -} diff --git a/futures-util/src/future/future/unit_error.rs b/futures-util/src/future/future/unit_error.rs deleted file mode 100644 index 679e988b16..0000000000 --- a/futures-util/src/future/future/unit_error.rs +++ /dev/null @@ -1,35 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct UnitError { - future: Fut, -} - -impl UnitError { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> UnitError { - UnitError { future } - } -} - -impl Unpin for UnitError {} - -impl FusedFuture for UnitError { - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for UnitError - where Fut: Future, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.future().poll(cx).map(Ok) - } -} diff --git a/futures-util/src/future/join.rs b/futures-util/src/future/join.rs index 5af5b408e9..363e119955 100644 --- a/futures-util/src/future/join.rs +++ b/futures-util/src/future/join.rs @@ -5,7 +5,8 @@ use core::fmt; use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; + use super::assert_future; macro_rules! generate { @@ -14,9 +15,10 @@ macro_rules! generate { ($Join:ident, <$($Fut:ident),*>), )*) => ($( $(#[$doc])* + #[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct $Join<$($Fut: Future),*> { - $($Fut: MaybeDone<$Fut>,)* + $(#[pin] $Fut: MaybeDone<$Fut>,)* } impl<$($Fut),*> fmt::Debug for $Join<$($Fut),*> @@ -39,24 +41,22 @@ macro_rules! generate { $($Fut: maybe_done($Fut)),* } } - $( - unsafe_pinned!($Fut: MaybeDone<$Fut>); - )* } impl<$($Fut: Future),*> Future for $Join<$($Fut),*> { type Output = ($($Fut::Output),*); fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { let mut all_done = true; + let mut futures = self.project(); $( - all_done &= self.as_mut().$Fut().poll(cx).is_ready(); + all_done &= futures.$Fut.as_mut().poll(cx).is_ready(); )* if all_done { - Poll::Ready(($(self.as_mut().$Fut().take_output().unwrap()), *)) + Poll::Ready(($(futures.$Fut.take_output().unwrap()), *)) } else { Poll::Pending } diff --git a/futures-util/src/future/join_all.rs b/futures-util/src/future/join_all.rs index 07408856a4..df62f3a1b5 100644 --- a/futures-util/src/future/join_all.rs +++ b/futures-util/src/future/join_all.rs @@ -10,42 +10,7 @@ use core::task::{Context, Poll}; use alloc::boxed::Box; use alloc::vec::Vec; -#[derive(Debug)] -enum ElemState -where - F: Future, -{ - Pending(F), - Done(Option), -} - -impl ElemState -where - F: Future, -{ - fn pending_pin_mut(self: Pin<&mut Self>) -> Option> { - // Safety: Basic enum pin projection, no drop + optionally Unpin based - // on the type of this variant - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(f) => Some(unsafe { Pin::new_unchecked(f) }), - ElemState::Done(_) => None, - } - } - - fn take_done(self: Pin<&mut Self>) -> Option { - // Safety: Going from pin to a variant we never pin-project - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(_) => None, - ElemState::Done(output) => output.take(), - } - } -} - -impl Unpin for ElemState -where - F: Future + Unpin, -{ -} +use super::MaybeDone; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's @@ -62,7 +27,7 @@ pub struct JoinAll where F: Future, { - elems: Pin]>>, + elems: Pin]>>, } impl fmt::Debug for JoinAll @@ -117,7 +82,7 @@ where I: IntoIterator, I::Item: Future, { - let elems: Box<[_]> = i.into_iter().map(ElemState::Pending).collect(); + let elems: Box<[_]> = i.into_iter().map(MaybeDone::Future).collect(); JoinAll { elems: elems.into() } } @@ -130,20 +95,16 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut all_done = true; - for mut elem in iter_pin_mut(self.elems.as_mut()) { - if let Some(pending) = elem.as_mut().pending_pin_mut() { - if let Poll::Ready(output) = pending.poll(cx) { - elem.set(ElemState::Done(Some(output))); - } else { - all_done = false; - } + for elem in iter_pin_mut(self.elems.as_mut()) { + if elem.poll(cx).is_pending() { + all_done = false; } } if all_done { let mut elems = mem::replace(&mut self.elems, Box::pin([])); let result = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_done().unwrap()) + .map(|e| e.take_output().unwrap()) .collect(); Poll::Ready(result) } else { diff --git a/futures-util/src/future/maybe_done.rs b/futures-util/src/future/maybe_done.rs index f16f889781..71cb6fa320 100644 --- a/futures-util/src/future/maybe_done.rs +++ b/futures-util/src/future/maybe_done.rs @@ -4,14 +4,16 @@ use core::mem; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; /// A future that may have completed. /// /// This is created by the [`maybe_done()`] function. +#[pin_project] #[derive(Debug)] pub enum MaybeDone { /// A not-yet-completed future - Future(Fut), + Future(#[pin] Fut), /// The output of the completed future Done(Fut::Output), /// The empty variant after the result of a [`MaybeDone`] has been @@ -19,9 +21,6 @@ pub enum MaybeDone { Gone, } -// Safe because we never generate `Pin<&mut Fut::Output>` -impl Unpin for MaybeDone {} - /// Wraps a future into a `MaybeDone` /// /// # Examples @@ -48,14 +47,13 @@ impl MaybeDone { /// 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. + #[project] #[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, - } + #[project] + match self.project() { + MaybeDone::Done(res) => Some(res), + _ => None, } } @@ -63,6 +61,11 @@ impl MaybeDone { /// towards completion. #[inline] pub fn take_output(self: Pin<&mut Self>) -> Option { + // Safety: we return immediately unless we are in the `Done` + // state, which does not have any pinning guarantees to uphold. + // + // Hopefully `pin_project` will support this safely soon: + // https://github.com/taiki-e/pin-project/issues/184 unsafe { let this = self.get_unchecked_mut(); match this { @@ -90,15 +93,17 @@ impl FusedFuture for MaybeDone { impl Future for MaybeDone { type Output = (); + #[project] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let res = unsafe { - match self.as_mut().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"), - } - }; - self.set(MaybeDone::Done(res)); + #[project] + match self.as_mut().project() { + MaybeDone::Future(f) => { + let res = ready!(f.poll(cx)); + self.set(MaybeDone::Done(res)); + }, + MaybeDone::Done(_) => {}, + MaybeDone::Gone => panic!("MaybeDone polled after value taken"), + } Poll::Ready(()) } } diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 3f4bb01436..7962894a99 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -10,13 +10,15 @@ pub use futures_core::future::{FusedFuture, Future, TryFuture}; pub use futures_task::{FutureObj, LocalFutureObj, UnsafeFutureObj}; // Extension traits and combinators - #[allow(clippy::module_inception)] mod future; pub use self::future::{ - Flatten, FlattenStream, Fuse, FutureExt, Inspect, IntoStream, Map, NeverError, Then, UnitError, + Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, NeverError, Then, UnitError, MapInto, }; +#[deprecated(note = "This is now an alias for [Flatten](Flatten)")] +pub use self::future::FlattenStream; + #[cfg(feature = "std")] pub use self::future::CatchUnwind; @@ -29,8 +31,8 @@ pub use self::future::Shared; mod try_future; pub use self::try_future::{ - AndThen, ErrInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, OrElse, TryFlattenStream, - TryFutureExt, UnwrapOrElse, + AndThen, ErrInto, OkInto, InspectErr, InspectOk, IntoFuture, MapErr, MapOk, OrElse, TryFlattenStream, + TryFutureExt, UnwrapOrElse, MapOkOrElse, TryFlatten, }; #[cfg(feature = "sink")] @@ -47,6 +49,9 @@ pub use self::pending::{pending, Pending}; mod maybe_done; pub use self::maybe_done::{maybe_done, MaybeDone}; +mod try_maybe_done; +pub use self::try_maybe_done::{try_maybe_done, TryMaybeDone}; + mod option; pub use self::option::OptionFuture; diff --git a/futures-util/src/future/option.rs b/futures-util/src/future/option.rs index 21413525d0..88be0099f4 100644 --- a/futures-util/src/future/option.rs +++ b/futures-util/src/future/option.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// A future representing a value which may or may not be present. /// @@ -22,15 +22,10 @@ use pin_utils::unsafe_pinned; /// assert_eq!(a.await, None); /// # }); /// ``` +#[pin_project] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OptionFuture { - option: Option, -} - -impl OptionFuture { - unsafe_pinned!(option: Option); -} +pub struct OptionFuture(#[pin] Option); impl Future for OptionFuture { type Output = Option; @@ -39,7 +34,7 @@ impl Future for OptionFuture { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - match self.option().as_pin_mut() { + match self.project().0.as_pin_mut() { Some(x) => x.poll(cx).map(Some), None => Poll::Ready(None), } @@ -48,7 +43,7 @@ impl Future for OptionFuture { impl FusedFuture for OptionFuture { fn is_terminated(&self) -> bool { - match &self.option { + match &self.0 { Some(x) => x.is_terminated(), None => true, } @@ -57,6 +52,6 @@ impl FusedFuture for OptionFuture { impl From> for OptionFuture { fn from(option: Option) -> Self { - OptionFuture { option } + OptionFuture(option) } } diff --git a/futures-util/src/future/try_future/and_then.rs b/futures-util/src/future/try_future/and_then.rs deleted file mode 100644 index 37333e0503..0000000000 --- a/futures-util/src/future/try_future/and_then.rs +++ /dev/null @@ -1,53 +0,0 @@ -use super::{TryChain, TryChainAction}; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`and_then`](super::TryFutureExt::and_then) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct AndThen { - try_chain: TryChain, -} - -impl AndThen - where Fut1: TryFuture, - Fut2: TryFuture, -{ - unsafe_pinned!(try_chain: TryChain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> AndThen { - AndThen { - try_chain: TryChain::new(future, f), - } - } -} - -impl FusedFuture for AndThen - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Ok) -> Fut2, -{ - fn is_terminated(&self) -> bool { - self.try_chain.is_terminated() - } -} - -impl Future for AndThen - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Ok) -> Fut2, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.try_chain().poll(cx, |result, async_op| { - match result { - Ok(ok) => TryChainAction::Future(async_op(ok)), - Err(err) => TryChainAction::Output(Err(err)), - } - }) - } -} diff --git a/futures-util/src/future/try_future/err_into.rs b/futures-util/src/future/try_future/err_into.rs deleted file mode 100644 index 731fcae39e..0000000000 --- a/futures-util/src/future/try_future/err_into.rs +++ /dev/null @@ -1,48 +0,0 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`err_into`](super::TryFutureExt::err_into) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ErrInto { - future: Fut, - _marker: PhantomData, -} - -impl Unpin for ErrInto {} - -impl ErrInto { - unsafe_pinned!(future: Fut); - - pub(super) fn new(future: Fut) -> ErrInto { - ErrInto { - future, - _marker: PhantomData, - } - } -} - -impl FusedFuture for ErrInto - where Fut: TryFuture + FusedFuture, - Fut::Error: Into, -{ - fn is_terminated(&self) -> bool { self.future.is_terminated() } -} - -impl Future for ErrInto - where Fut: TryFuture, - Fut::Error: Into, -{ - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.future().try_poll(cx) - .map(|res| res.map_err(Into::into)) - } -} diff --git a/futures-util/src/future/try_future/flatten_sink.rs b/futures-util/src/future/try_future/flatten_sink.rs deleted file mode 100644 index d6863dd2cd..0000000000 --- a/futures-util/src/future/try_future/flatten_sink.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::FlattenStreamSink; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Sink for the [`flatten_sink`](super::TryFutureExt::flatten_sink) method. -#[derive(Debug)] -#[must_use = "sinks do nothing unless polled"] -pub struct FlattenSink -where - Fut: TryFuture, -{ - inner: FlattenStreamSink, -} - -impl FlattenSink -where - Fut: TryFuture, -{ - unsafe_pinned!(inner: FlattenStreamSink); - - pub(super) fn new(future: Fut) -> Self { - Self { - inner: FlattenStreamSink::new(future), - } - } -} - -impl FusedStream for FlattenSink -where - Fut: TryFuture, - S: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -impl Stream for FlattenSink -where - Fut: TryFuture, - S: TryStream, -{ - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } -} - -impl Sink for FlattenSink -where - Fut: TryFuture, - Si: Sink, -{ - type Error = Fut::Error; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_ready(cx) - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - self.inner().start_send(item) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } -} diff --git a/futures-util/src/future/try_future/flatten_stream_sink.rs b/futures-util/src/future/try_future/flatten_stream_sink.rs deleted file mode 100644 index 5a56bf708d..0000000000 --- a/futures-util/src/future/try_future/flatten_stream_sink.rs +++ /dev/null @@ -1,181 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -#[must_use = "streams do nothing unless polled"] -pub(crate) struct FlattenStreamSink -where - Fut: TryFuture, -{ - state: State, -} - -impl Unpin for FlattenStreamSink -where - Fut: TryFuture + Unpin, - Fut::Ok: Unpin, -{ -} - -impl fmt::Debug for FlattenStreamSink -where - Fut: TryFuture + fmt::Debug, - Fut::Ok: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlattenStreamSink") - .field("state", &self.state) - .finish() - } -} - -impl FlattenStreamSink -where - Fut: TryFuture, -{ - unsafe_pinned!(state: State); - - pub(crate) fn new(future: Fut) -> Self { - Self { - state: State::Future(future), - } - } -} - -#[derive(Debug)] -enum State { - // future is not yet called or called and not ready - Future(Fut), - // future resolved to Stream or Sink - StreamOrSink(S), - // future resolved to error - Done, -} - -impl State { - fn get_pin_mut(self: Pin<&mut Self>) -> State, Pin<&mut S>> { - // safety: data is never moved via the resulting &mut reference - match unsafe { self.get_unchecked_mut() } { - // safety: the future we're re-pinning here will never be moved; - // it will just be polled, then dropped in place - State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }), - // safety: the stream we're repinning here will never be moved; - // it will just be polled, then dropped in place - State::StreamOrSink(s) => State::StreamOrSink(unsafe { Pin::new_unchecked(s) }), - State::Done => State::Done, - } - } -} - -impl State -where - Fut: TryFuture, -{ - fn poll_future(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let State::Future(f) = self.as_mut().get_pin_mut() { - match ready!(f.try_poll(cx)) { - Ok(s) => { - // Future resolved to stream. - // We do not return, but poll that - // stream in the next loop iteration. - self.set(State::StreamOrSink(s)); - } - Err(e) => { - // Future resolved to error. - // We have neither a pollable stream nor a future. - self.set(State::Done); - return Poll::Ready(Err(e)); - } - } - } - Poll::Ready(Ok(())) - } -} - -impl FusedStream for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - match &self.state { - State::Future(_) => false, - State::StreamOrSink(stream) => stream.is_terminated(), - State::Done => true, - } - } -} - -impl Stream for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - type Item = Result<::Ok, Fut::Error>; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - ready!(self.as_mut().state().poll_future(cx)?); - match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.try_poll_next(cx), - State::Done => Poll::Ready(None), - State::Future(_) => unreachable!(), - } - } -} - -#[cfg(feature = "sink")] -impl Sink for FlattenStreamSink -where - Fut: TryFuture, - Fut::Ok: Sink, -{ - type Error = Fut::Error; - - fn poll_ready( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - ready!(self.as_mut().state().poll_future(cx)?); - match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_ready(cx), - State::Done => panic!("poll_ready called after eof"), - State::Future(_) => unreachable!(), - } - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - match self.state().get_pin_mut() { - State::StreamOrSink(s) => s.start_send(item), - State::Future(_) => panic!("poll_ready not called first"), - State::Done => panic!("start_send called after eof"), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_flush(cx), - // if sink not yet resolved, nothing written ==> everything flushed - State::Future(_) => Poll::Ready(Ok(())), - State::Done => panic!("poll_flush called after eof"), - } - } - - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - let res = match self.as_mut().state().get_pin_mut() { - State::StreamOrSink(s) => s.poll_close(cx), - State::Future(_) | State::Done => Poll::Ready(Ok(())), - }; - if res.is_ready() { - self.as_mut().state().set(State::Done); - } - res - } -} diff --git a/futures-util/src/future/try_future/inspect_err.rs b/futures-util/src/future/try_future/inspect_err.rs deleted file mode 100644 index 8700337bb2..0000000000 --- a/futures-util/src/future/try_future/inspect_err.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct InspectErr { - future: Fut, - f: Option, -} - -impl Unpin for InspectErr {} - -impl InspectErr -where - Fut: TryFuture, - F: FnOnce(&Fut::Error), -{ - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Self { - Self { future, f: Some(f) } - } -} - -impl FusedFuture for InspectErr -where - Fut: TryFuture + FusedFuture, - F: FnOnce(&Fut::Error), -{ - fn is_terminated(&self) -> bool { - self.future.is_terminated() - } -} - -impl Future for InspectErr -where - Fut: TryFuture, - F: FnOnce(&Fut::Error), -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().try_poll(cx)); - if let Err(e) = &e { - self.as_mut().f().take().expect("cannot poll InspectErr twice")(e); - } - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/try_future/inspect_ok.rs b/futures-util/src/future/try_future/inspect_ok.rs deleted file mode 100644 index 3d0a972226..0000000000 --- a/futures-util/src/future/try_future/inspect_ok.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct InspectOk { - future: Fut, - f: Option, -} - -impl Unpin for InspectOk {} - -impl InspectOk -where - Fut: TryFuture, - F: FnOnce(&Fut::Ok), -{ - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - pub(super) fn new(future: Fut, f: F) -> Self { - Self { future, f: Some(f) } - } -} - -impl FusedFuture for InspectOk -where - Fut: TryFuture + FusedFuture, - F: FnOnce(&Fut::Ok), -{ - fn is_terminated(&self) -> bool { - self.future.is_terminated() - } -} - -impl Future for InspectOk -where - Fut: TryFuture, - F: FnOnce(&Fut::Ok), -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let e = ready!(self.as_mut().future().try_poll(cx)); - if let Ok(e) = &e { - self.as_mut().f().take().expect("cannot poll InspectOk twice")(e); - } - Poll::Ready(e) - } -} diff --git a/futures-util/src/future/try_future/into_future.rs b/futures-util/src/future/try_future/into_future.rs index a766d5b66d..240bb1bcb9 100644 --- a/futures-util/src/future/try_future/into_future.rs +++ b/futures-util/src/future/try_future/into_future.rs @@ -1,26 +1,23 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Future for the [`into_future`](super::TryFutureExt::into_future) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct IntoFuture { - future: Fut, -} +pub struct IntoFuture(#[pin] Fut); impl IntoFuture { - unsafe_pinned!(future: Fut); - #[inline] - pub(super) fn new(future: Fut) -> IntoFuture { - IntoFuture { future } + pub(crate) fn new(future: Fut) -> IntoFuture { + IntoFuture(future) } } impl FusedFuture for IntoFuture { - fn is_terminated(&self) -> bool { self.future.is_terminated() } + fn is_terminated(&self) -> bool { self.0.is_terminated() } } impl Future for IntoFuture { @@ -31,6 +28,6 @@ impl Future for IntoFuture { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - self.future().try_poll(cx) + self.project().0.try_poll(cx) } } diff --git a/futures-util/src/future/try_future/map_err.rs b/futures-util/src/future/try_future/map_err.rs deleted file mode 100644 index 8edebad86d..0000000000 --- a/futures-util/src/future/try_future/map_err.rs +++ /dev/null @@ -1,52 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_err`](super::TryFutureExt::map_err) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapErr { - future: Fut, - f: Option, -} - -impl MapErr { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new MapErr. - pub(super) fn new(future: Fut, f: F) -> MapErr { - MapErr { future, f: Some(f) } - } -} - -impl Unpin for MapErr {} - -impl FusedFuture for MapErr - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> E, -{ - fn is_terminated(&self) -> bool { self.f.is_none() } -} - -impl Future for MapErr - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> E, -{ - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let f = self.as_mut().f().take() - .expect("MapErr must not be polled after it returned `Poll::Ready`"); - result.map_err(f) - }) - } -} diff --git a/futures-util/src/future/try_future/map_ok.rs b/futures-util/src/future/try_future/map_ok.rs deleted file mode 100644 index ab28f1443f..0000000000 --- a/futures-util/src/future/try_future/map_ok.rs +++ /dev/null @@ -1,54 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_ok`](super::TryFutureExt::map_ok) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapOk { - future: Fut, - f: Option, -} - -impl MapOk { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new MapOk. - pub(super) fn new(future: Fut, f: F) -> MapOk { - MapOk { future, f: Some(f) } - } -} - -impl Unpin for MapOk {} - -impl FusedFuture for MapOk - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() - } -} - -impl Future for MapOk - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, -{ - type Output = Result; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let op = self.as_mut().f().take() - .expect("MapOk must not be polled after it returned `Poll::Ready`"); - result.map(op) - }) - } -} diff --git a/futures-util/src/future/try_future/map_ok_or_else.rs b/futures-util/src/future/try_future/map_ok_or_else.rs deleted file mode 100644 index 730b67922c..0000000000 --- a/futures-util/src/future/try_future/map_ok_or_else.rs +++ /dev/null @@ -1,59 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`map_ok_or_else`](super::TryFutureExt::map_ok_or_else) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct MapOkOrElse { - future: Fut, - f: Option, - e: Option, -} - -impl MapOkOrElse { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - unsafe_unpinned!(e: Option); - - /// Creates a new MapOkOrElse. - pub(super) fn new(future: Fut, e: E, f: F) -> Self { - Self { future, f: Some(f), e: Some(e) } - } -} - -impl Unpin for MapOkOrElse {} - -impl FusedFuture for MapOkOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, - E: FnOnce(Fut::Error) -> T, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() || self.e.is_none() - } -} - -impl Future for MapOkOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Ok) -> T, - E: FnOnce(Fut::Error) -> T, -{ - type Output = T; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - match result { - Ok(i) => (self.as_mut().f().take().expect("MapOkOrElse must not be polled after it returned `Poll::Ready`"))(i), - Err(e) => (self.as_mut().e().take().expect("MapOkOrElse must not be polled after it returned `Poll::Ready`"))(e), - } - }) - } -} diff --git a/futures-util/src/future/try_future/mod.rs b/futures-util/src/future/try_future/mod.rs index e8e059e373..4f4a4cc1d6 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -14,65 +14,120 @@ use futures_core::{ #[cfg(feature = "sink")] use futures_sink::Sink; -// Combinators - -mod and_then; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::and_then::AndThen; +use super::assert_future; +use crate::future::{Map, Inspect}; +use crate::fns::{ + MapOkFn, map_ok_fn, MapErrFn, map_err_fn, MapOkOrElseFn, + map_ok_or_else_fn, IntoFn, UnwrapOrElseFn, unwrap_or_else_fn, InspectOkFn, inspect_ok_fn, InspectErrFn, + inspect_err_fn, into_fn +}; -mod err_into; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::err_into::ErrInto; +// Combinators +mod into_future; +mod try_flatten; +mod try_flatten_err; + +delegate_all!( + /// Future for the [`try_flatten`](TryFutureExt::try_flatten) method. + TryFlatten( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten::TryFlatten::new(x)] +); + +delegate_all!( + /// Future for the [`try_flatten_err`](TryFutureExt::try_flatten_err) method. + TryFlattenErr( + try_flatten_err::TryFlattenErr + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] +); + +delegate_all!( + /// Future for the [`try_flatten_stream`](TryFutureExt::try_flatten_stream) method. + TryFlattenStream( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] +); #[cfg(feature = "sink")] -mod flatten_sink; -#[cfg(feature = "sink")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten_sink::FlattenSink; - -mod inspect_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_ok::InspectOk; +delegate_all!( + /// Sink for the [`flatten_sink`](TryFutureExt::flatten_sink) method. + FlattenSink( + try_flatten::TryFlatten + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] +); + +delegate_all!( + /// Future for the [`and_then`](TryFutureExt::and_then) method. + AndThen( + TryFlatten, Fut2> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] +); + +delegate_all!( + /// Future for the [`or_else`](TryFutureExt::or_else) method. + OrElse( + TryFlattenErr, Fut2> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] +); + +delegate_all!( + /// Future for the [`err_into`](TryFutureExt::err_into) method. + ErrInto( + MapErr> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapErr::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`ok_into`](TryFutureExt::ok_into) method. + OkInto( + MapOk> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapOk::new(x, into_fn())] +); + +delegate_all!( + /// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. + InspectOk( + Inspect, InspectOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] +); + +delegate_all!( + /// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. + InspectErr( + Inspect, InspectErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] +); -mod inspect_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_err::InspectErr; - -mod into_future; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_future::IntoFuture; -mod map_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_err::MapErr; - -mod map_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok::MapOk; - -mod map_ok_or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok_or_else::MapOkOrElse; - -mod or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::or_else::OrElse; - -mod try_flatten_stream; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::try_flatten_stream::TryFlattenStream; - -mod unwrap_or_else; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::unwrap_or_else::UnwrapOrElse; - -// Implementation details - -mod flatten_stream_sink; -pub(crate) use self::flatten_stream_sink::FlattenStreamSink; - -mod try_chain; -pub(crate) use self::try_chain::{TryChain, TryChainAction}; +delegate_all!( + /// Future for the [`map_ok`](TryFutureExt::map_ok) method. + MapOk( + Map, MapOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] +); + +delegate_all!( + /// Future for the [`map_err`](TryFutureExt::map_err) method. + MapErr( + Map, MapErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] +); + +delegate_all!( + /// Future for the [`map_ok_or_else`](TryFutureExt::map_ok_or_else) method. + MapOkOrElse( + Map, MapOkOrElseFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] +); + +delegate_all!( + /// Future for the [`unwrap_or_else`](TryFutureExt::unwrap_or_else) method. + UnwrapOrElse( + Map, UnwrapOrElseFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] +); impl TryFutureExt for Fut {} @@ -161,7 +216,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(Self::Ok) -> T, Self: Sized, { - MapOk::new(self, f) + assert_future::, _>(MapOk::new(self, f)) } /// Maps this future's success value to a different value, and permits for error handling resulting in the same type. @@ -202,7 +257,7 @@ pub trait TryFutureExt: TryFuture { E: FnOnce(Self::Error) -> T, Self: Sized, { - MapOkOrElse::new(self, e, f) + assert_future::(MapOkOrElse::new(self, f, e)) } /// Maps this future's error value to a different value. @@ -249,7 +304,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(Self::Error) -> E, Self: Sized, { - MapErr::new(self, f) + assert_future::, _>(MapErr::new(self, f)) } /// Maps this future's [`Error`](TryFuture::Error) to a new error type @@ -279,7 +334,17 @@ pub trait TryFutureExt: TryFuture { Self: Sized, Self::Error: Into, { - ErrInto::new(self) + assert_future::, _>(ErrInto::new(self)) + } + + /// Maps this future's [`Ok`](TryFuture::Ok) to a new type + /// using the [`Into`](std::convert::Into) trait. + fn ok_into(self) -> OkInto + where + Self: Sized, + Self::Ok: Into, + { + assert_future::, _>(OkInto::new(self)) } /// Executes another future after this one resolves successfully. The @@ -324,7 +389,7 @@ pub trait TryFutureExt: TryFuture { Fut: TryFuture, Self: Sized, { - AndThen::new(self, f) + assert_future::, _>(AndThen::new(self, f)) } /// Executes another future if this one resolves to an error. The @@ -369,7 +434,7 @@ pub trait TryFutureExt: TryFuture { Fut: TryFuture, Self: Sized, { - OrElse::new(self, f) + assert_future::, _>(OrElse::new(self, f)) } /// Do something with the success value of a future before passing it on. @@ -395,7 +460,7 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(&Self::Ok), Self: Sized, { - InspectOk::new(self, f) + assert_future::, _>(InspectOk::new(self, f)) } /// Do something with the error value of a future before passing it on. @@ -421,7 +486,19 @@ pub trait TryFutureExt: TryFuture { F: FnOnce(&Self::Error), Self: Sized, { - InspectErr::new(self, f) + assert_future::, _>(InspectErr::new(self, f)) + } + + /// Flatten the execution of this future when the successful result of this + /// future is another future. + /// + /// This is equivalent to `future.and_then(|x| x)`. + fn try_flatten(self) -> TryFlatten + where + Self::Ok: TryFuture, + Self: Sized, + { + TryFlatten::new(self) } /// Flatten the execution of this future when the successful result of this @@ -449,7 +526,7 @@ pub trait TryFutureExt: TryFuture { /// assert_eq!(list, Ok(vec![17, 18, 19])); /// # }); /// ``` - fn try_flatten_stream(self) -> TryFlattenStream + fn try_flatten_stream(self) -> TryFlattenStream where Self::Ok: TryStream, Self: Sized, @@ -484,7 +561,7 @@ pub trait TryFutureExt: TryFuture { Self: Sized, F: FnOnce(Self::Error) -> Self::Ok, { - UnwrapOrElse::new(self, f) + assert_future::(UnwrapOrElse::new(self, f)) } /// Wraps a [`TryFuture`] into a future compatable with libraries using diff --git a/futures-util/src/future/try_future/or_else.rs b/futures-util/src/future/try_future/or_else.rs deleted file mode 100644 index a9c006fa9f..0000000000 --- a/futures-util/src/future/try_future/or_else.rs +++ /dev/null @@ -1,56 +0,0 @@ -use super::{TryChain, TryChainAction}; -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; - -/// Future for the [`or_else`](super::TryFutureExt::or_else) method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct OrElse { - try_chain: TryChain, -} - -impl OrElse - where Fut1: TryFuture, - Fut2: TryFuture, -{ - unsafe_pinned!(try_chain: TryChain); - - /// Creates a new `Then`. - pub(super) fn new(future: Fut1, f: F) -> OrElse { - OrElse { - try_chain: TryChain::new(future, f), - } - } -} - -impl FusedFuture for OrElse - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Error) -> Fut2, -{ - fn is_terminated(&self) -> bool { - self.try_chain.is_terminated() - } -} - -impl Future for OrElse - where Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(Fut1::Error) -> Fut2, -{ - type Output = Result; - - fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.try_chain().poll(cx, |result, async_op| { - match result { - Ok(ok) => TryChainAction::Output(Ok(ok)), - Err(err) => TryChainAction::Future(async_op(err)), - } - }) - } -} diff --git a/futures-util/src/future/try_future/try_chain.rs b/futures-util/src/future/try_future/try_chain.rs deleted file mode 100644 index ddd275a609..0000000000 --- a/futures-util/src/future/try_future/try_chain.rs +++ /dev/null @@ -1,110 +0,0 @@ -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::task::{Context, Poll}; - -#[must_use = "futures do nothing unless you `.await` or poll them"] -#[derive(Debug)] -pub(crate) enum TryChain { - First(Fut1, Option), - Second(Fut2), - Empty, -} - -impl Unpin for TryChain {} - -pub(crate) enum TryChainAction - where Fut2: TryFuture, -{ - Future(Fut2), - Output(Result), -} - -impl TryChain - where Fut1: TryFuture, - Fut2: TryFuture, -{ - pub(crate) fn new(fut1: Fut1, data: Data) -> TryChain { - TryChain::First(fut1, Some(data)) - } - - pub(crate) fn is_terminated(&self) -> bool { - match self { - TryChain::First(..) | TryChain::Second(_) => false, - TryChain::Empty => true, - } - } - - pub(crate) fn poll( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - f: F, - ) -> Poll> - where F: FnOnce(Result, Data) -> TryChainAction, - { - let mut f = Some(f); - - // Safe to call `get_unchecked_mut` because we won't move the futures. - let this = unsafe { self.get_unchecked_mut() }; - - loop { - let (output, data) = match this { - TryChain::First(fut1, data) => { - // Poll the first future - let output = ready!(unsafe { Pin::new_unchecked(fut1) }.try_poll(cx)); - (output, data.take().unwrap()) - } - TryChain::Second(fut2) => { - // Poll the second future - return unsafe { Pin::new_unchecked(fut2) } - .try_poll(cx) - .map(|res| { - *this = TryChain::Empty; // Drop fut2. - res - }); - } - TryChain::Empty => { - panic!("future must not be polled after it returned `Poll::Ready`"); - } - }; - - *this = TryChain::Empty; // Drop fut1 - let f = f.take().unwrap(); - match f(output, data) { - TryChainAction::Future(fut2) => *this = TryChain::Second(fut2), - TryChainAction::Output(output) => return Poll::Ready(output), - } - } - } -} - -#[cfg(test)] -mod tests { - - #[cfg(feature = "std")] // dont test with no_std - #[test] - fn try_chain_is_terminated() { - use std::pin::Pin; - use std::task::Poll; - - use futures_test::task::noop_context; - - use crate::future::ready; - - use super::{TryChain, TryChainAction}; - - let mut cx = noop_context(); - - let mut future = TryChain::new(ready(Ok(1)), ()); - assert!(!future.is_terminated()); - - let res = Pin::new(&mut future).poll( - &mut cx, - |res: Result, ()| { - assert!(res.is_ok()); - TryChainAction::Future(ready(Ok(2))) - }, - ); - assert_eq!(res, Poll::Ready::>(Ok(2))); - assert!(future.is_terminated()); - } -} diff --git a/futures-util/src/future/try_future/try_flatten.rs b/futures-util/src/future/try_future/try_flatten.rs new file mode 100644 index 0000000000..428377a196 --- /dev/null +++ b/futures-util/src/future/try_future/try_flatten.rs @@ -0,0 +1,180 @@ +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::stream::{FusedStream, Stream, TryStream}; +#[cfg(feature = "sink")] +use futures_sink::Sink; +use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; + +#[pin_project] +#[derive(Debug)] +pub enum TryFlatten { + First(#[pin] Fut1), + Second(#[pin] Fut2), + Empty, +} + +impl TryFlatten { + pub(crate) fn new(future: Fut1) -> Self { + TryFlatten::First(future) + } +} + +impl FusedFuture for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryFuture, +{ + fn is_terminated(&self) -> bool { + match self { + TryFlatten::Empty => true, + _ => false, + } + } +} + +impl Future for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryFuture, +{ + type Output = Result<::Ok, Fut::Error>; + + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlatten::First(f) => { + match ready!(f.try_poll(cx)) { + Ok(f) => self.set(TryFlatten::Second(f)), + Err(e) => { + self.set(TryFlatten::Empty); + break Err(e); + } + } + }, + TryFlatten::Second(f) => { + let output = ready!(f.try_poll(cx)); + self.set(TryFlatten::Empty); + break output; + }, + TryFlatten::Empty => return Poll::Pending, + } + }) + } +} + +impl FusedStream for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryStream, +{ + fn is_terminated(&self) -> bool { + match self { + TryFlatten::Empty => true, + _ => false, + } + } +} + +impl Stream for TryFlatten + where Fut: TryFuture, + Fut::Ok: TryStream, +{ + type Item = Result<::Ok, Fut::Error>; + + #[project] + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlatten::First(f) => { + match ready!(f.try_poll(cx)) { + Ok(f) => self.set(TryFlatten::Second(f)), + Err(e) => { + self.set(TryFlatten::Empty); + break Some(Err(e)); + } + } + }, + TryFlatten::Second(f) => { + let output = ready!(f.try_poll_next(cx)); + if output.is_none() { + self.set(TryFlatten::Empty); + } + break output; + }, + TryFlatten::Empty => break None, + } + }) + } +} + + +#[cfg(feature = "sink")] +impl Sink for TryFlatten +where + Fut: TryFuture, + Fut::Ok: Sink, +{ + type Error = Fut::Error; + + #[project] + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlatten::First(f) => { + match ready!(f.try_poll(cx)) { + Ok(f) => self.set(TryFlatten::Second(f)), + Err(e) => { + self.set(TryFlatten::Empty); + break Err(e); + } + } + }, + TryFlatten::Second(f) => { + break ready!(f.poll_ready(cx)); + }, + TryFlatten::Empty => panic!("poll_ready called after eof"), + } + }) + } + + #[project] + fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + #[project] + match self.project() { + TryFlatten::First(_) => panic!("poll_ready not called first"), + TryFlatten::Second(f) => f.start_send(item), + TryFlatten::Empty => panic!("start_send called after eof"), + } + } + + #[project] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + match self.project() { + TryFlatten::First(_) => Poll::Ready(Ok(())), + TryFlatten::Second(f) => f.poll_flush(cx), + TryFlatten::Empty => panic!("poll_flush called after eof"), + } + } + + #[project] + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + #[project] + let res = match self.as_mut().project() { + TryFlatten::Second(f) => f.poll_close(cx), + _ => Poll::Ready(Ok(())), + }; + if res.is_ready() { + self.set(TryFlatten::Empty); + } + res + } +} diff --git a/futures-util/src/future/try_future/try_flatten_err.rs b/futures-util/src/future/try_future/try_flatten_err.rs new file mode 100644 index 0000000000..b4c54efb67 --- /dev/null +++ b/futures-util/src/future/try_future/try_flatten_err.rs @@ -0,0 +1,61 @@ +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; + +#[pin_project] +#[derive(Debug)] +pub enum TryFlattenErr { + First(#[pin] Fut1), + Second(#[pin] Fut2), + Empty, +} + +impl TryFlattenErr { + pub(crate) fn new(future: Fut1) -> Self { + TryFlattenErr::First(future) + } +} + +impl FusedFuture for TryFlattenErr + where Fut: TryFuture, + Fut::Error: TryFuture, +{ + fn is_terminated(&self) -> bool { + match self { + TryFlattenErr::Empty => true, + _ => false, + } + } +} + +impl Future for TryFlattenErr + where Fut: TryFuture, + Fut::Error: TryFuture, +{ + type Output = Result::Error>; + + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(loop { + #[project] + match self.as_mut().project() { + TryFlattenErr::First(f) => { + match ready!(f.try_poll(cx)) { + Err(f) => self.set(TryFlattenErr::Second(f)), + Ok(e) => { + self.set(TryFlattenErr::Empty); + break Ok(e); + } + } + }, + TryFlattenErr::Second(f) => { + let output = ready!(f.try_poll(cx)); + self.set(TryFlattenErr::Empty); + break output; + }, + TryFlattenErr::Empty => return Poll::Pending, + } + }) + } +} diff --git a/futures-util/src/future/try_future/try_flatten_stream.rs b/futures-util/src/future/try_future/try_flatten_stream.rs deleted file mode 100644 index 24624314c0..0000000000 --- a/futures-util/src/future/try_future/try_flatten_stream.rs +++ /dev/null @@ -1,91 +0,0 @@ -use super::FlattenStreamSink; -use core::fmt; -use core::pin::Pin; -use futures_core::future::TryFuture; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`try_flatten_stream`](super::TryFutureExt::try_flatten_stream) method. -#[must_use = "streams do nothing unless polled"] -pub struct TryFlattenStream -where - Fut: TryFuture, -{ - inner: FlattenStreamSink, -} - -impl TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - unsafe_pinned!(inner: FlattenStreamSink); - - pub(super) fn new(future: Fut) -> Self { - Self { - inner: FlattenStreamSink::new(future), - } - } -} - -impl fmt::Debug for TryFlattenStream -where - Fut: TryFuture + fmt::Debug, - Fut::Ok: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TryFlattenStream") - .field("inner", &self.inner) - .finish() - } -} - -impl FusedStream for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream + FusedStream, -{ - fn is_terminated(&self) -> bool { - self.inner.is_terminated() - } -} - -impl Stream for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream, -{ - type Item = Result<::Ok, Fut::Error>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_next(cx) - } -} - -#[cfg(feature = "sink")] -impl Sink for TryFlattenStream -where - Fut: TryFuture, - Fut::Ok: TryStream + Sink, -{ - type Error = Fut::Error; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_ready(cx) - } - - fn start_send(self: Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { - self.inner().start_send(item) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } -} diff --git a/futures-util/src/future/try_future/unwrap_or_else.rs b/futures-util/src/future/try_future/unwrap_or_else.rs deleted file mode 100644 index 286cc009fb..0000000000 --- a/futures-util/src/future/try_future/unwrap_or_else.rs +++ /dev/null @@ -1,55 +0,0 @@ -use core::pin::Pin; -use futures_core::future::{FusedFuture, Future, TryFuture}; -use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Future for the [`unwrap_or_else`](super::TryFutureExt::unwrap_or_else) -/// method. -#[derive(Debug)] -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct UnwrapOrElse { - future: Fut, - f: Option, -} - -impl UnwrapOrElse { - unsafe_pinned!(future: Fut); - unsafe_unpinned!(f: Option); - - /// Creates a new UnwrapOrElse. - pub(super) fn new(future: Fut, f: F) -> UnwrapOrElse { - UnwrapOrElse { future, f: Some(f) } - } -} - -impl Unpin for UnwrapOrElse {} - -impl FusedFuture for UnwrapOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> Fut::Ok, -{ - fn is_terminated(&self) -> bool { - self.f.is_none() - } -} - -impl Future for UnwrapOrElse - where Fut: TryFuture, - F: FnOnce(Fut::Error) -> Fut::Ok, -{ - type Output = Fut::Ok; - - fn poll( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll { - self.as_mut() - .future() - .try_poll(cx) - .map(|result| { - let op = self.as_mut().f().take() - .expect("UnwrapOrElse already returned `Poll::Ready` before"); - result.unwrap_or_else(op) - }) - } -} diff --git a/futures-util/src/future/try_join.rs b/futures-util/src/future/try_join.rs index da85eff91d..b4a3b98c1f 100644 --- a/futures-util/src/future/try_join.rs +++ b/futures-util/src/future/try_join.rs @@ -1,11 +1,11 @@ #![allow(non_snake_case)] -use crate::future::{MaybeDone, maybe_done, TryFutureExt, IntoFuture}; +use crate::future::{TryMaybeDone, try_maybe_done}; use core::fmt; use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; macro_rules! generate { ($( @@ -13,10 +13,11 @@ macro_rules! generate { ($Join:ident, ), )*) => ($( $(#[$doc])* + #[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct $Join { - Fut1: MaybeDone>, - $($Fut: MaybeDone>,)* + #[pin] Fut1: TryMaybeDone, + $(#[pin] $Fut: TryMaybeDone<$Fut>,)* } impl fmt::Debug for $Join @@ -47,15 +48,10 @@ macro_rules! generate { { fn new(Fut1: Fut1, $($Fut: $Fut),*) -> $Join { $Join { - Fut1: maybe_done(TryFutureExt::into_future(Fut1)), - $($Fut: maybe_done(TryFutureExt::into_future($Fut))),* + Fut1: try_maybe_done(Fut1), + $($Fut: try_maybe_done($Fut)),* } } - - unsafe_pinned!(Fut1: MaybeDone>); - $( - unsafe_pinned!($Fut: MaybeDone>); - )* } impl Future for $Join @@ -68,29 +64,20 @@ macro_rules! generate { type Output = Result<(Fut1::Ok, $($Fut::Ok),*), Fut1::Error>; fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { let mut all_done = true; - if self.as_mut().Fut1().poll(cx).is_pending() { - all_done = false; - } else if self.as_mut().Fut1().output_mut().unwrap().is_err() { - return Poll::Ready(Err( - self.as_mut().Fut1().take_output().unwrap().err().unwrap())); - } + let mut futures = self.project(); + all_done &= futures.Fut1.as_mut().poll(cx)?.is_ready(); $( - if self.as_mut().$Fut().poll(cx).is_pending() { - all_done = false; - } else if self.as_mut().$Fut().output_mut().unwrap().is_err() { - return Poll::Ready(Err( - self.as_mut().$Fut().take_output().unwrap().err().unwrap())); - } + all_done &= futures.$Fut.as_mut().poll(cx)?.is_ready(); )* if all_done { Poll::Ready(Ok(( - self.as_mut().Fut1().take_output().unwrap().ok().unwrap(), + futures.Fut1.take_output().unwrap(), $( - self.as_mut().$Fut().take_output().unwrap().ok().unwrap() + futures.$Fut.take_output().unwrap() ),* ))) } else { diff --git a/futures-util/src/future/try_join_all.rs b/futures-util/src/future/try_join_all.rs index 30300e4e3e..4de0a79b94 100644 --- a/futures-util/src/future/try_join_all.rs +++ b/futures-util/src/future/try_join_all.rs @@ -10,40 +10,7 @@ use core::task::{Context, Poll}; use alloc::boxed::Box; use alloc::vec::Vec; -use super::TryFuture; - -#[derive(Debug)] -enum ElemState -where - F: TryFuture, -{ - Pending(F), - Done(Option), -} - -impl ElemState -where - F: TryFuture, -{ - fn pending_pin_mut(self: Pin<&mut Self>) -> Option> { - // Safety: Basic enum pin projection, no drop + optionally Unpin based - // on the type of this variant - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(f) => Some(unsafe { Pin::new_unchecked(f) }), - ElemState::Done(_) => None, - } - } - - fn take_done(self: Pin<&mut Self>) -> Option { - // Safety: Going from pin to a variant we never pin-project - match unsafe { self.get_unchecked_mut() } { - ElemState::Pending(_) => None, - ElemState::Done(output) => output.take(), - } - } -} - -impl Unpin for ElemState where F: TryFuture + Unpin {} +use super::{TryFuture, TryMaybeDone}; fn iter_pin_mut(slice: Pin<&mut [T]>) -> impl Iterator> { // Safety: `std` _could_ make this unsound if it were to decide Pin's @@ -66,7 +33,7 @@ pub struct TryJoinAll where F: TryFuture, { - elems: Pin]>>, + elems: Pin]>>, } impl fmt::Debug for TryJoinAll @@ -125,7 +92,7 @@ where I: IntoIterator, I::Item: TryFuture, { - let elems: Box<[_]> = i.into_iter().map(ElemState::Pending).collect(); + let elems: Box<[_]> = i.into_iter().map(TryMaybeDone::Future).collect(); TryJoinAll { elems: elems.into(), } @@ -140,17 +107,13 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut state = FinalState::AllDone; - for mut elem in iter_pin_mut(self.elems.as_mut()) { - if let Some(pending) = elem.as_mut().pending_pin_mut() { - match pending.try_poll(cx) { - Poll::Pending => state = FinalState::Pending, - Poll::Ready(output) => match output { - Ok(item) => elem.set(ElemState::Done(Some(item))), - Err(e) => { - state = FinalState::Error(e); - break; - } - } + for elem in iter_pin_mut(self.elems.as_mut()) { + match elem.try_poll(cx) { + Poll::Pending => state = FinalState::Pending, + Poll::Ready(Ok(())) => {}, + Poll::Ready(Err(e)) => { + state = FinalState::Error(e); + break; } } } @@ -160,7 +123,7 @@ where FinalState::AllDone => { let mut elems = mem::replace(&mut self.elems, Box::pin([])); let results = iter_pin_mut(elems.as_mut()) - .map(|e| e.take_done().unwrap()) + .map(|e| e.take_output().unwrap()) .collect(); Poll::Ready(Ok(results)) }, diff --git a/futures-util/src/future/try_maybe_done.rs b/futures-util/src/future/try_maybe_done.rs new file mode 100644 index 0000000000..a249c5ce33 --- /dev/null +++ b/futures-util/src/future/try_maybe_done.rs @@ -0,0 +1,99 @@ +//! Definition of the TryMaybeDone combinator + +use core::mem; +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future, TryFuture}; +use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; + +/// A future that may have completed with an error. +/// +/// This is created by the [`try_maybe_done()`] function. +#[pin_project] +#[derive(Debug)] +pub enum TryMaybeDone { + /// A not-yet-completed future + Future(#[pin] Fut), + /// The output of the completed future + Done(Fut::Ok), + /// The empty variant after the result of a [`TryMaybeDone`] has been + /// taken using the [`take_output`](TryMaybeDone::take_output) method, + /// or if the future returned an error. + Gone, +} + +/// Wraps a future into a `TryMaybeDone` +pub fn try_maybe_done(future: Fut) -> TryMaybeDone { + TryMaybeDone::Future(future) +} + +impl TryMaybeDone { + /// 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 completed successfully and [`take_output`](TryMaybeDone::take_output) + /// has not yet been called. + #[project] + #[inline] + pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Ok> { + #[project] + match self.project() { + TryMaybeDone::Done(res) => Some(res), + _ => None, + } + } + + /// Attempt to take the output of a `TryMaybeDone` without driving it + /// towards completion. + #[inline] + pub fn take_output(self: Pin<&mut Self>) -> Option { + // Safety: we return immediately unless we are in the `Done` + // state, which does not have any pinning guarantees to uphold. + // + // Hopefully `pin_project` will support this safely soon: + // https://github.com/taiki-e/pin-project/issues/184 + unsafe { + let this = self.get_unchecked_mut(); + match this { + TryMaybeDone::Done(_) => {}, + TryMaybeDone::Future(_) | TryMaybeDone::Gone => return None, + }; + if let TryMaybeDone::Done(output) = mem::replace(this, TryMaybeDone::Gone) { + Some(output) + } else { + unreachable!() + } + } + } +} + +impl FusedFuture for TryMaybeDone { + fn is_terminated(&self) -> bool { + match self { + TryMaybeDone::Future(_) => false, + TryMaybeDone::Done(_) | TryMaybeDone::Gone => true, + } + } +} + +impl Future for TryMaybeDone { + type Output = Result<(), Fut::Error>; + + #[project] + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + match self.as_mut().project() { + TryMaybeDone::Future(f) => { + match ready!(f.try_poll(cx)) { + Ok(res) => self.set(TryMaybeDone::Done(res)), + Err(e) => { + self.set(TryMaybeDone::Gone); + return Poll::Ready(Err(e)); + } + } + }, + TryMaybeDone::Done(_) => {}, + TryMaybeDone::Gone => panic!("TryMaybeDone polled after value taken"), + } + Poll::Ready(Ok(())) + } +} diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs index 96d3f2815e..94b3d2aefa 100644 --- a/futures-util/src/io/buf_reader.rs +++ b/futures-util/src/io/buf_reader.rs @@ -1,8 +1,8 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSliceMut, SeekFrom}; +use pin_project::{pin_project, project}; use std::io::{self, Read}; use std::pin::Pin; use std::{cmp, fmt}; @@ -27,19 +27,15 @@ use super::DEFAULT_BUF_SIZE; /// [`AsyncRead`]: futures_io::AsyncRead /// // TODO: Examples +#[pin_project] pub struct BufReader { + #[pin] inner: R, - buf: Box<[u8]>, + buffer: Box<[u8]>, pos: usize, cap: usize, } -impl BufReader { - unsafe_pinned!(inner: R); - unsafe_unpinned!(pos: usize); - unsafe_unpinned!(cap: usize); -} - impl BufReader { /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. @@ -55,53 +51,30 @@ impl BufReader { super::initialize(&inner, &mut buffer); Self { inner, - buf: buffer.into_boxed_slice(), + buffer: buffer.into_boxed_slice(), pos: 0, cap: 0, } } } - /// Gets a reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { - self.inner() - } - - /// Consumes this `BufWriter`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> R { - self.inner - } + delegate_access_inner!(inner, R, ()); /// Returns a reference to the internally buffered data. /// /// Unlike `fill_buf`, this will not attempt to fill the buffer if it is empty. pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.cap] + &self.buffer[self.pos..self.cap] } /// Invalidates all data in the internal buffer. + #[project] #[inline] - fn discard_buffer(mut self: Pin<&mut Self>) { - *self.as_mut().pos() = 0; - *self.cap() = 0; + fn discard_buffer(self: Pin<&mut Self>) { + #[project] + let BufReader { pos, cap, .. } = self.project(); + *pos = 0; + *cap = 0; } } @@ -114,8 +87,8 @@ impl AsyncRead for BufReader { // If we don't have any buffered data and we're doing a massive read // (larger than our internal buffer), bypass our internal buffer // entirely. - if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = ready!(self.as_mut().inner().poll_read(cx, buf)); + if self.pos == self.cap && buf.len() >= self.buffer.len() { + let res = ready!(self.as_mut().project().inner.poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } @@ -131,8 +104,8 @@ impl AsyncRead for BufReader { bufs: &mut [IoSliceMut<'_>], ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.pos == self.cap && total_len >= self.buf.len() { - let res = ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + if self.pos == self.cap && total_len >= self.buffer.len() { + let res = ready!(self.as_mut().project().inner.poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } @@ -150,12 +123,13 @@ impl AsyncRead for BufReader { } impl AsyncBufRead for BufReader { + #[project] fn poll_fill_buf( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let Self { inner, buf, cap, pos } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + #[project] + let BufReader { inner, buffer, cap, pos } = self.project(); // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. @@ -163,48 +137,26 @@ impl AsyncBufRead for BufReader { // to tell the compiler that the pos..cap slice is always valid. if *pos >= *cap { debug_assert!(*pos == *cap); - *cap = ready!(inner.as_mut().poll_read(cx, buf))?; + *cap = ready!(inner.poll_read(cx, buffer))?; *pos = 0; } - Poll::Ready(Ok(&buf[*pos..*cap])) + Poll::Ready(Ok(&buffer[*pos..*cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { - *self.as_mut().pos() = cmp::min(self.pos + amt, self.cap); + fn consume(self: Pin<&mut Self>, amt: usize) { + *self.project().pos = cmp::min(self.pos + amt, self.cap); } } impl AsyncWrite for BufReader { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - self.inner().poll_write(cx, buf) - } - - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - self.inner().poll_write_vectored(cx, bufs) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_flush(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.inner().poll_close(cx) - } + delegate_async_write!(inner); } impl fmt::Debug for BufReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BufReader") .field("reader", &self.inner) - .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) + .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buffer.len())) .finish() } } @@ -242,16 +194,16 @@ impl AsyncSeek for BufReader { // support seeking by i64::min_value() so we need to handle underflow when subtracting // remainder. if let Some(offset) = n.checked_sub(remainder) { - result = ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(offset)))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(offset)))?; } else { // seek backwards by our remainder, and then by the offset - ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(-remainder)))?; + ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(-remainder)))?; self.as_mut().discard_buffer(); - result = ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, SeekFrom::Current(n)))?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = ready!(self.as_mut().project().inner.poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs index b0afbd81f2..66df81f5a4 100644 --- a/futures-util/src/io/buf_writer.rs +++ b/futures-util/src/io/buf_writer.rs @@ -1,8 +1,6 @@ use futures_core::task::{Context, Poll}; -#[cfg(feature = "read-initializer")] -use futures_io::Initializer; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, IoSliceMut, SeekFrom}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, IoSlice, SeekFrom}; +use pin_project::{pin_project, project}; use std::fmt; use std::io::{self, Write}; use std::pin::Pin; @@ -29,17 +27,14 @@ use super::DEFAULT_BUF_SIZE; /// [`flush`]: super::AsyncWriteExt::flush /// // TODO: Examples +#[pin_project] pub struct BufWriter { + #[pin] inner: W, buf: Vec, written: usize, } -impl BufWriter { - unsafe_pinned!(inner: W); - unsafe_unpinned!(buf: Vec); -} - impl BufWriter { /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// but may change in the future. @@ -56,9 +51,10 @@ impl BufWriter { } } + #[project] fn flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, buf, written } = unsafe { self.get_unchecked_mut() }; - let mut inner = unsafe { Pin::new_unchecked(inner) }; + #[project] + let BufWriter { mut inner, buf, written } = self.project(); let len = buf.len(); let mut ret = Ok(()); @@ -85,31 +81,7 @@ impl BufWriter { Poll::Ready(ret) } - /// Gets a reference to the underlying writer. - pub fn get_ref(&self) -> &W { - &self.inner - } - - /// Gets a mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - pub fn get_mut(&mut self) -> &mut W { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> { - self.inner() - } - - /// Consumes this `BufWriter`, returning the underlying writer. - /// - /// Note that any leftover data in the internal buffer is lost. - pub fn into_inner(self) -> W { - self.inner - } + delegate_access_inner!(inner, W, ()); /// Returns a reference to the internally buffered data. pub fn buffer(&self) -> &[u8] { @@ -127,9 +99,9 @@ impl AsyncWrite for BufWriter { ready!(self.as_mut().flush_buf(cx))?; } if buf.len() >= self.buf.capacity() { - self.inner().poll_write(cx, buf) + self.project().inner.poll_write(cx, buf) } else { - Poll::Ready(self.buf().write(buf)) + Poll::Ready(self.project().buf.write(buf)) } } @@ -143,58 +115,29 @@ impl AsyncWrite for BufWriter { ready!(self.as_mut().flush_buf(cx))?; } if total_len >= self.buf.capacity() { - self.inner().poll_write_vectored(cx, bufs) + self.project().inner.poll_write_vectored(cx, bufs) } else { - Poll::Ready(self.buf().write_vectored(bufs)) + Poll::Ready(self.project().buf.write_vectored(bufs)) } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_flush(cx) + self.project().inner.poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_close(cx) + self.project().inner.poll_close(cx) } } impl AsyncRead for BufWriter { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - self.inner().poll_read(cx, buf) - } - - fn poll_read_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - self.inner().poll_read_vectored(cx, bufs) - } - - // we can't skip unconditionally because of the large buffer case in read. - #[cfg(feature = "read-initializer")] - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } + delegate_async_read!(inner); } impl AsyncBufRead for BufWriter { - fn poll_fill_buf( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.inner().poll_fill_buf(cx) - } - - fn consume(self: Pin<&mut Self>, amt: usize) { - self.inner().consume(amt) - } + delegate_async_buf_read!(inner); } impl fmt::Debug for BufWriter { @@ -217,6 +160,6 @@ impl AsyncSeek for BufWriter { pos: SeekFrom, ) -> Poll> { ready!(self.as_mut().flush_buf(cx))?; - self.inner().poll_seek(cx, pos) + self.project().inner.poll_seek(cx, pos) } } diff --git a/futures-util/src/io/chain.rs b/futures-util/src/io/chain.rs index 64bbdec87a..4e85854a41 100644 --- a/futures-util/src/io/chain.rs +++ b/futures-util/src/io/chain.rs @@ -2,35 +2,27 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; use futures_io::{AsyncBufRead, AsyncRead, IoSliceMut}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use std::fmt; use std::io; use std::pin::Pin; /// Reader for the [`chain`](super::AsyncReadExt::chain) method. +#[pin_project] #[must_use = "readers do nothing unless polled"] pub struct Chain { + #[pin] first: T, + #[pin] second: U, done_first: bool, } -impl Unpin for Chain -where - T: Unpin, - U: Unpin, -{ -} - impl Chain where T: AsyncRead, U: AsyncRead, { - unsafe_pinned!(first: T); - unsafe_pinned!(second: U); - unsafe_unpinned!(done_first: bool); - pub(super) fn new(first: T, second: U) -> Self { Self { first, @@ -90,34 +82,42 @@ where T: AsyncRead, U: AsyncRead, { + #[project] fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if !self.done_first { - match ready!(self.as_mut().first().poll_read(cx, buf)?) { - 0 if !buf.is_empty() => *self.as_mut().done_first() = true, + #[project] + let Chain { first, second, done_first } = self.project(); + + if !*done_first { + match ready!(first.poll_read(cx, buf)?) { + 0 if !buf.is_empty() => *done_first = true, n => return Poll::Ready(Ok(n)), } } - self.second().poll_read(cx, buf) + second.poll_read(cx, buf) } + #[project] fn poll_read_vectored( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - if !self.done_first { - let n = ready!(self.as_mut().first().poll_read_vectored(cx, bufs)?); + #[project] + let Chain { first, second, done_first } = self.project(); + + if !*done_first { + let n = ready!(first.poll_read_vectored(cx, bufs)?); if n == 0 && bufs.iter().any(|b| !b.is_empty()) { - *self.as_mut().done_first() = true + *done_first = true } else { return Poll::Ready(Ok(n)); } } - self.second().poll_read_vectored(cx, bufs) + second.poll_read_vectored(cx, bufs) } #[cfg(feature = "read-initializer")] @@ -136,14 +136,10 @@ where T: AsyncBufRead, U: AsyncBufRead, { + #[project] fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - first, - second, - done_first, - } = unsafe { self.get_unchecked_mut() }; - let first = unsafe { Pin::new_unchecked(first) }; - let second = unsafe { Pin::new_unchecked(second) }; + #[project] + let Chain { first, second, done_first } = self.project(); if !*done_first { match ready!(first.poll_fill_buf(cx)?) { @@ -156,11 +152,15 @@ where second.poll_fill_buf(cx) } + #[project] fn consume(self: Pin<&mut Self>, amt: usize) { - if !self.done_first { - self.first().consume(amt) + #[project] + let Chain { first, second, done_first } = self.project(); + + if !*done_first { + first.consume(amt) } else { - self.second().consume(amt) + second.consume(amt) } } } diff --git a/futures-util/src/io/copy.rs b/futures-util/src/io/copy.rs index 9531aab996..491a680a4a 100644 --- a/futures-util/src/io/copy.rs +++ b/futures-util/src/io/copy.rs @@ -4,7 +4,7 @@ use futures_io::{AsyncRead, AsyncWrite}; use std::io; use std::pin::Pin; use super::{BufReader, copy_buf, CopyBuf}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Creates a future which copies all the bytes from one object to another. /// @@ -42,22 +42,18 @@ where } /// Future for the [`copy()`] function. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Copy<'a, R, W: ?Sized> { + #[pin] inner: CopyBuf<'a, BufReader, W>, } -impl<'a, R: AsyncRead, W: ?Sized> Unpin for Copy<'a, R, W> where CopyBuf<'a, BufReader, W>: Unpin {} - -impl<'a, R: AsyncRead, W: ?Sized> Copy<'a, R, W> { - unsafe_pinned!(inner: CopyBuf<'a, BufReader, W>); -} - impl Future for Copy<'_, R, W> { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.inner().poll(cx) + self.project().inner.poll(cx) } } diff --git a/futures-util/src/io/copy_buf.rs b/futures-util/src/io/copy_buf.rs index 98811825e0..f8bb49ea0a 100644 --- a/futures-util/src/io/copy_buf.rs +++ b/futures-util/src/io/copy_buf.rs @@ -3,6 +3,7 @@ use futures_core::task::{Context, Poll}; use futures_io::{AsyncBufRead, AsyncWrite}; use std::io; use std::pin::Pin; +use pin_project::{pin_project, project}; /// Creates a future which copies all the bytes from one object to another. /// @@ -42,41 +43,34 @@ where } /// Future for the [`copy_buf()`] function. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct CopyBuf<'a, R, W: ?Sized> { + #[pin] reader: R, writer: &'a mut W, amt: u64, } -impl Unpin for CopyBuf<'_, R, W> {} - -impl CopyBuf<'_, R, W> { - fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.reader), Pin::new(&mut *this.writer), &mut this.amt) - } - } -} - impl Future for CopyBuf<'_, R, W> where R: AsyncBufRead, W: AsyncWrite + Unpin + ?Sized, { type Output = io::Result; + #[project] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let (mut reader, mut writer, amt) = self.project(); + #[project] + let CopyBuf { mut reader, mut writer, amt } = self.project(); loop { let buffer = ready!(reader.as_mut().poll_fill_buf(cx))?; if buffer.is_empty() { - ready!(writer.as_mut().poll_flush(cx))?; + ready!(Pin::new(&mut writer).poll_flush(cx))?; return Poll::Ready(Ok(*amt)); } - let i = ready!(writer.as_mut().poll_write(cx, buffer))?; + let i = ready!(Pin::new(&mut writer).poll_write(cx, buffer))?; if i == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) } diff --git a/futures-util/src/io/into_sink.rs b/futures-util/src/io/into_sink.rs index bdc6b34140..0589f3abca 100644 --- a/futures-util/src/io/into_sink.rs +++ b/futures-util/src/io/into_sink.rs @@ -3,8 +3,7 @@ use futures_io::AsyncWrite; use futures_sink::Sink; use std::io; use std::pin::Pin; -use std::marker::Unpin; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; #[derive(Debug)] struct Block { @@ -13,40 +12,33 @@ struct Block { } /// Sink for the [`into_sink`](super::AsyncWriteExt::into_sink) method. +#[pin_project] #[must_use = "sinks do nothing unless polled"] #[derive(Debug)] pub struct IntoSink { + #[pin] writer: W, /// An outstanding block for us to push into the underlying writer, along with an offset of how /// far into this block we have written already. buffer: Option>, } -impl Unpin for IntoSink {} - impl> IntoSink { - unsafe_pinned!(writer: W); - unsafe_unpinned!(buffer: Option>); - pub(super) fn new(writer: W) -> Self { IntoSink { writer, buffer: None } } - fn project(self: Pin<&mut Self>) -> (Pin<&mut W>, &mut Option>) { - unsafe { - let this = self.get_unchecked_mut(); - (Pin::new_unchecked(&mut this.writer), &mut this.buffer) - } - } - /// If we have an outstanding block in `buffer` attempt to push it into the writer, does _not_ /// flush the writer after it succeeds in pushing the block into it. + #[project] fn poll_flush_buffer( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let (mut writer, buffer) = self.project(); + #[project] + let IntoSink { mut writer, buffer } = self.project(); + if let Some(buffer) = buffer { loop { let bytes = buffer.bytes.as_ref(); @@ -67,22 +59,22 @@ impl> Sink for IntoSink { type Error = io::Error; fn poll_ready( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - ready!(self.as_mut().poll_flush_buffer(cx))?; + ready!(self.poll_flush_buffer(cx))?; Poll::Ready(Ok(())) } #[allow(clippy::debug_assert_with_mut_call)] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - debug_assert!(self.as_mut().buffer().is_none()); - *self.as_mut().buffer() = Some(Block { offset: 0, bytes: item }); + debug_assert!(self.buffer.is_none()); + *self.project().buffer = Some(Block { offset: 0, bytes: item }); Ok(()) } @@ -92,7 +84,7 @@ impl> Sink for IntoSink { ) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; - ready!(self.as_mut().writer().poll_flush(cx))?; + ready!(self.project().writer.poll_flush(cx))?; Poll::Ready(Ok(())) } @@ -102,7 +94,7 @@ impl> Sink for IntoSink { ) -> Poll> { ready!(self.as_mut().poll_flush_buffer(cx))?; - ready!(self.as_mut().writer().poll_close(cx))?; + ready!(self.project().writer.poll_close(cx))?; Poll::Ready(Ok(())) } } diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs index 2e1261689b..af0b491c76 100644 --- a/futures-util/src/io/lines.rs +++ b/futures-util/src/io/lines.rs @@ -5,19 +5,21 @@ use std::io; use std::mem; use std::pin::Pin; use super::read_line::read_line_internal; +use pin_project::{pin_project, project}; /// Stream for the [`lines`](super::AsyncBufReadExt::lines) method. + +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Lines { + #[pin] reader: R, buf: String, bytes: Vec, read: usize, } -impl Unpin for Lines {} - impl Lines { pub(super) fn new(reader: R) -> Self { Self { @@ -32,9 +34,10 @@ impl Lines { impl Stream for Lines { type Item = io::Result; + #[project] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { reader, buf, bytes, read } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; + #[project] + let Lines { reader, buf, bytes, read } = self.project(); let n = ready!(read_line_internal(reader, cx, buf, bytes, read))?; if n == 0 && buf.is_empty() { return Poll::Ready(None) diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs index d830514b92..81d8415131 100644 --- a/futures-util/src/io/read_line.rs +++ b/futures-util/src/io/read_line.rs @@ -23,7 +23,7 @@ impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadLine<'a, R> { pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self { Self { reader, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + bytes: mem::replace(buf, String::new()).into_bytes(), buf, read: 0, } diff --git a/futures-util/src/io/read_to_string.rs b/futures-util/src/io/read_to_string.rs index 56c95ce18d..113fe6af33 100644 --- a/futures-util/src/io/read_to_string.rs +++ b/futures-util/src/io/read_to_string.rs @@ -23,7 +23,7 @@ impl<'a, R: AsyncRead + ?Sized + Unpin> ReadToString<'a, R> { let start_len = buf.len(); Self { reader, - bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) }, + bytes: mem::replace(buf, String::new()).into_bytes(), buf, start_len, } diff --git a/futures-util/src/io/take.rs b/futures-util/src/io/take.rs index b1f33fa468..39088b71ed 100644 --- a/futures-util/src/io/take.rs +++ b/futures-util/src/io/take.rs @@ -2,25 +2,22 @@ use futures_core::task::{Context, Poll}; #[cfg(feature = "read-initializer")] use futures_io::Initializer; use futures_io::{AsyncRead, AsyncBufRead}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use std::{cmp, io}; use std::pin::Pin; /// Reader for the [`take`](super::AsyncReadExt::take) method. +#[pin_project] #[derive(Debug)] #[must_use = "readers do nothing unless you `.await` or poll them"] pub struct Take { + #[pin] inner: R, // Add '_' to avoid conflicts with `limit` method. limit_: u64, } -impl Unpin for Take { } - impl Take { - unsafe_pinned!(inner: R); - unsafe_unpinned!(limit_: u64); - pub(super) fn new(inner: R, limit: u64) -> Self { Self { inner, limit_: limit } } @@ -82,101 +79,26 @@ impl Take { self.limit_ = limit } - /// Gets a reference to the underlying reader. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor_ref = take.get_ref(); - /// assert_eq!(cursor_ref.position(), 4); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor_mut = take.get_mut(); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Gets a pinned mutable reference to the underlying reader. - /// - /// Care should be taken to avoid modifying the internal I/O state of the - /// underlying reader as doing so may corrupt the internal limit of this - /// `Take`. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> { - self.inner() - } - - /// Consumes the `Take`, returning the wrapped reader. - /// - /// # Examples - /// - /// ``` - /// # futures::executor::block_on(async { - /// use futures::io::{AsyncReadExt, Cursor}; - /// - /// let reader = Cursor::new(&b"12345678"[..]); - /// let mut buffer = [0; 4]; - /// - /// let mut take = reader.take(4); - /// let n = take.read(&mut buffer).await?; - /// - /// let cursor = take.into_inner(); - /// assert_eq!(cursor.position(), 4); - /// - /// # Ok::<(), Box>(()) }).unwrap(); - /// ``` - pub fn into_inner(self) -> R { - self.inner - } + delegate_access_inner!(inner, R, ()); } impl AsyncRead for Take { + #[project] fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - if self.limit_ == 0 { + #[project] + let Take { inner, limit_ } = self.project(); + + if *limit_ == 0 { return Poll::Ready(Ok(0)); } - let max = std::cmp::min(buf.len() as u64, self.limit_) as usize; - let n = ready!(self.as_mut().inner().poll_read(cx, &mut buf[..max]))?; - *self.as_mut().limit_() -= n as u64; + let max = std::cmp::min(buf.len() as u64, *limit_) as usize; + let n = ready!(inner.poll_read(cx, &mut buf[..max]))?; + *limit_ -= n as u64; Poll::Ready(Ok(n)) } @@ -187,9 +109,10 @@ impl AsyncRead for Take { } impl AsyncBufRead for Take { + #[project] fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { inner, limit_ } = unsafe { self.get_unchecked_mut() }; - let inner = unsafe { Pin::new_unchecked(inner) }; + #[project] + let Take { inner, limit_ } = self.project(); // Don't call into inner reader at all at EOF because it may still block if *limit_ == 0 { @@ -201,10 +124,14 @@ impl AsyncBufRead for Take { Poll::Ready(Ok(&buf[..cap])) } - fn consume(mut self: Pin<&mut Self>, amt: usize) { + #[project] + fn consume(self: Pin<&mut Self>, amt: usize) { + #[project] + let Take { inner, limit_ } = self.project(); + // Don't let callers reset the limit by passing an overlarge value - let amt = cmp::min(amt as u64, self.limit_) as usize; - *self.as_mut().limit_() -= amt as u64; - self.inner().consume(amt); + let amt = cmp::min(amt as u64, *limit_) as usize; + *limit_ -= amt as u64; + inner.consume(amt); } } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 13827170b5..e8e9e0b0c3 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -7,8 +7,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -// It cannot be included in the published code because this lints have false positives in the minimum required version. -#![cfg_attr(test, warn(single_use_lifetimes))] #![warn(clippy::all)] // The solution for this lint is not available on 1.39 which is the current minimum supported version. @@ -71,35 +69,238 @@ macro_rules! cfg_target_has_atomic { macro_rules! delegate_sink { ($field:ident, $item:ty) => { fn poll_ready( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, cx: &mut $crate::core_reexport::task::Context<'_>, ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_ready(cx) + self.project().$field.poll_ready(cx) } fn start_send( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, item: $item, ) -> Result<(), Self::Error> { - self.$field().start_send(item) + self.project().$field.start_send(item) } fn poll_flush( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, cx: &mut $crate::core_reexport::task::Context<'_>, ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_flush(cx) + self.project().$field.poll_flush(cx) } fn poll_close( - self: Pin<&mut Self>, + self: core::pin::Pin<&mut Self>, cx: &mut $crate::core_reexport::task::Context<'_>, ) -> $crate::core_reexport::task::Poll> { - self.$field().poll_close(cx) + self.project().$field.poll_close(cx) } } } +macro_rules! delegate_future { + ($field:ident) => { + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut $crate::core_reexport::task::Context<'_>, + ) -> $crate::core_reexport::task::Poll { + self.project().$field.poll(cx) + } + } +} + +macro_rules! delegate_stream { + ($field:ident) => { + fn poll_next( + self: core::pin::Pin<&mut Self>, + cx: &mut $crate::core_reexport::task::Context<'_>, + ) -> $crate::core_reexport::task::Poll> { + self.project().$field.poll_next(cx) + } + fn size_hint(&self) -> (usize, Option) { + self.$field.size_hint() + } + } +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_write { + ($field:ident) => { + fn poll_write(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, buf: &[u8]) + -> core::task::Poll> + { + self.project().$field.poll_write(cx, buf) + } + fn poll_write_vectored(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, bufs: &[std::io::IoSlice<'_>]) + -> core::task::Poll> + { + self.project().$field.poll_write_vectored(cx, bufs) + } + fn poll_flush(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) + -> core::task::Poll> + { + self.project().$field.poll_flush(cx) + } + fn poll_close(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) + -> core::task::Poll> + { + self.project().$field.poll_close(cx) + } + } +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_read { + ($field:ident) => { + #[cfg(feature = "read-initializer")] + unsafe fn initializer(&self) -> $crate::io::Initializer { + self.$field.initializer() + } + + fn poll_read(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, buf: &mut [u8]) + -> core::task::Poll> + { + self.project().$field.poll_read(cx, buf) + } + + fn poll_read_vectored(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, bufs: &mut [std::io::IoSliceMut<'_>]) + -> core::task::Poll> + { + self.project().$field.poll_read_vectored(cx, bufs) + } + } +} + +#[cfg(feature = "io")] +#[cfg(feature = "std")] +macro_rules! delegate_async_buf_read { + ($field:ident) => { + fn poll_fill_buf( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll> { + self.project().$field.poll_fill_buf(cx) + } + + fn consume(self: core::pin::Pin<&mut Self>, amt: usize) { + self.project().$field.consume(amt) + } + } +} + +macro_rules! delegate_access_inner { + ($field:ident, $inner:ty, ($($ind:tt)*)) => { + /// Acquires a reference to the underlying sink or stream that this combinator is + /// pulling from. + pub fn get_ref(&self) -> &$inner { + (&self.$field) $($ind get_ref())* + } + + /// Acquires a mutable reference to the underlying sink or stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// sink or stream which may otherwise confuse this combinator. + pub fn get_mut(&mut self) -> &mut $inner { + (&mut self.$field) $($ind get_mut())* + } + + /// Acquires a pinned mutable reference to the underlying sink or stream that this + /// combinator is pulling from. + /// + /// Note that care must be taken to avoid tampering with the state of the + /// sink or stream which may otherwise confuse this combinator. + pub fn get_pin_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut $inner> { + self.project().$field $($ind get_pin_mut())* + } + + /// Consumes this combinator, returning the underlying sink or stream. + /// + /// Note that this may discard intermediate state of this combinator, so + /// care should be taken to avoid losing resources when this is called. + pub fn into_inner(self) -> $inner { + self.$field $($ind into_inner())* + } + } +} + +macro_rules! delegate_all { + (@trait Future $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::future::Future for $name<$($arg),*> where $t: futures_core::future::Future $(, $($bound)*)* { + type Output = <$t as futures_core::future::Future>::Output; + + delegate_future!(inner); + } + }; + (@trait FusedFuture $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::future::FusedFuture for $name<$($arg),*> where $t: futures_core::future::FusedFuture $(, $($bound)*)* { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } + } + }; + (@trait Stream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::stream::Stream for $name<$($arg),*> where $t: futures_core::stream::Stream $(, $($bound)*)* { + type Item = <$t as futures_core::stream::Stream>::Item; + + delegate_stream!(inner); + } + }; + (@trait FusedStream $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> futures_core::stream::FusedStream for $name<$($arg),*> where $t: futures_core::stream::FusedStream $(, $($bound)*)* { + fn is_terminated(&self) -> bool { + self.inner.is_terminated() + } + } + }; + (@trait Sink $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + #[cfg(feature = "sink")] + impl<_Item, $($arg),*> futures_sink::Sink<_Item> for $name<$($arg),*> where $t: futures_sink::Sink<_Item> $(, $($bound)*)* { + type Error = <$t as futures_sink::Sink<_Item>>::Error; + + delegate_sink!(inner, _Item); + } + }; + (@trait Debug $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> core::fmt::Debug for $name<$($arg),*> where $t: core::fmt::Debug $(, $($bound)*)* { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.inner, f) + } + } + }; + (@trait AccessInner[$inner:ty, ($($ind:tt)*)] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* { + delegate_access_inner!(inner, $inner, ($($ind)*)); + } + }; + (@trait New[|$($param:ident: $paramt:ty),*| $cons:expr] $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => { + impl<$($arg),*> $name<$($arg),*> $(where $($bound)*)* { + pub(crate) fn new($($param: $paramt),*) -> Self { + Self { inner: $cons } + } + } + }; + ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($targs:tt)*])* $({$($item:tt)*})* $(where $($bound:tt)*)*) => { + #[pin_project::pin_project] + #[must_use = "futures/streams/sinks do nothing unless you `.await` or poll them"] + $(#[$attr])* + pub struct $name< $($arg),* > $(where $($bound)*)* { #[pin] inner:$t } + + impl<$($arg),*> $name< $($arg),* > $(where $($bound)*)* { + $($($item)*)* + } + + delegate_all!(@trait $ftrait $([$($targs)*])* $name<$($arg),*>($t) $(where $($bound)*)*); + }; + ($(#[$attr:meta])* $name:ident<$($arg:ident),*>($t:ty) : $ftrait:ident $([$($ftargs:tt)*])* + $strait:ident $([$($stargs:tt)*])* $(+ $trait:ident $([$($targs:tt)*])*)* $({$($item:tt)*})* $(where $($bound:tt)*)*) => { + delegate_all!($(#[$attr])* $name<$($arg),*>($t) : $strait $([$($stargs)*])* $(+ $trait $([$($targs)*])*)* $({$($item)*})* $(where $($bound)*)*); + + delegate_all!(@trait $ftrait $([$($ftargs)*])* $name<$($arg),*>($t) $(where $($bound)*)*); + }; +} + pub mod future; #[doc(hidden)] pub use crate::future::{FutureExt, TryFutureExt}; @@ -125,6 +326,9 @@ pub mod io; #[cfg(feature = "std")] #[doc(hidden)] pub use crate::io::{AsyncReadExt, AsyncWriteExt, AsyncSeekExt, AsyncBufReadExt}; +mod fns; + + cfg_target_has_atomic! { #[cfg(feature = "alloc")] pub mod lock; diff --git a/futures-util/src/sink/buffer.rs b/futures-util/src/sink/buffer.rs index d2a3f9098b..c3df3b93c9 100644 --- a/futures-util/src/sink/buffer.rs +++ b/futures-util/src/sink/buffer.rs @@ -1,14 +1,16 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::pin::Pin; use alloc::collections::VecDeque; /// Sink for the [`buffer`](super::SinkExt::buffer) method. +#[pin_project] #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] pub struct Buffer { + #[pin] sink: Si, buf: VecDeque, @@ -16,13 +18,7 @@ pub struct Buffer { capacity: usize, } -impl Unpin for Buffer {} - impl, Item> Buffer { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(buf: VecDeque); - unsafe_unpinned!(capacity: usize); - pub(super) fn new(sink: Si, capacity: usize) -> Self { Buffer { sink, @@ -31,38 +27,20 @@ impl, Item> Buffer { } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); + #[project] fn try_empty_buffer( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - ready!(self.as_mut().sink().poll_ready(cx))?; - while let Some(item) = self.as_mut().buf().pop_front() { - self.as_mut().sink().start_send(item)?; - if !self.buf.is_empty() { - ready!(self.as_mut().sink().poll_ready(cx))?; + #[project] + let Buffer { mut sink, buf, .. } = self.project(); + ready!(sink.as_mut().poll_ready(cx))?; + while let Some(item) = buf.pop_front() { + sink.as_mut().start_send(item)?; + if !buf.is_empty() { + ready!(sink.as_mut().poll_ready(cx))?; } } Poll::Ready(Ok(())) @@ -74,7 +52,7 @@ impl Stream for Buffer where S: Sink + Stream { type Item = S::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.sink().poll_next(cx) + self.project().sink.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -96,7 +74,7 @@ impl, Item> Sink for Buffer { cx: &mut Context<'_>, ) -> Poll> { if self.capacity == 0 { - return self.as_mut().sink().poll_ready(cx); + return self.project().sink.poll_ready(cx); } let _ = self.as_mut().try_empty_buffer(cx)?; @@ -109,13 +87,13 @@ impl, Item> Sink for Buffer { } fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { if self.capacity == 0 { - self.as_mut().sink().start_send(item) + self.project().sink.start_send(item) } else { - self.as_mut().buf().push_back(item); + self.project().buf.push_back(item); Ok(()) } } @@ -126,8 +104,8 @@ impl, Item> Sink for Buffer { cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; - debug_assert!(self.as_mut().buf().is_empty()); - self.as_mut().sink().poll_flush(cx) + debug_assert!(self.buf.is_empty()); + self.project().sink.poll_flush(cx) } #[allow(clippy::debug_assert_with_mut_call)] @@ -136,7 +114,7 @@ impl, Item> Sink for Buffer { cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_buffer(cx))?; - debug_assert!(self.as_mut().buf().is_empty()); - self.as_mut().sink().poll_close(cx) + debug_assert!(self.buf.is_empty()); + self.project().sink.poll_close(cx) } } diff --git a/futures-util/src/sink/err_into.rs b/futures-util/src/sink/err_into.rs index af198feaa0..530e1ccf42 100644 --- a/futures-util/src/sink/err_into.rs +++ b/futures-util/src/sink/err_into.rs @@ -1,14 +1,14 @@ use crate::sink::{SinkExt, SinkMapErr}; -use core::pin::Pin; use futures_core::stream::{Stream, FusedStream}; -use futures_core::task::{Context, Poll}; use futures_sink::{Sink}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project}; /// Sink for the [`sink_err_into`](super::SinkExt::sink_err_into) method. +#[pin_project] #[derive(Debug)] #[must_use = "sinks do nothing unless polled"] pub struct SinkErrInto, Item, E> { + #[pin] sink: SinkMapErr E>, } @@ -16,36 +16,13 @@ impl SinkErrInto where Si: Sink, Si::Error: Into, { - unsafe_pinned!(sink: SinkMapErr E>); - pub(super) fn new(sink: Si) -> Self { SinkErrInto { sink: SinkExt::sink_map_err(sink, Into::into), } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - self.sink.get_ref() - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - self.sink.get_mut() - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink.into_inner() - } + delegate_access_inner!(sink, Si, (.)); } impl Sink for SinkErrInto @@ -64,16 +41,7 @@ impl Stream for SinkErrInto { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for SinkErrInto diff --git a/futures-util/src/sink/fanout.rs b/futures-util/src/sink/fanout.rs index 24e4de95eb..7066e216fb 100644 --- a/futures-util/src/sink/fanout.rs +++ b/futures-util/src/sink/fanout.rs @@ -2,22 +2,22 @@ use core::fmt::{Debug, Formatter, Result as FmtResult}; use core::pin::Pin; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Sink that clones incoming items and forwards them to two sinks at the same time. /// /// Backpressure from any downstream sink propagates up, which means that this sink /// can only process items as fast as its _slowest_ downstream sink. +#[pin_project] #[must_use = "sinks do nothing unless polled"] pub struct Fanout { + #[pin] sink1: Si1, + #[pin] sink2: Si2 } impl Fanout { - unsafe_pinned!(sink1: Si1); - unsafe_pinned!(sink2: Si2); - pub(super) fn new(sink1: Si1, sink2: Si2) -> Fanout { Fanout { sink1, sink2 } } @@ -33,11 +33,11 @@ impl Fanout { } /// Get a pinned mutable reference to the inner sinks. + #[project] pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut Si1>, Pin<&mut Si2>) { - unsafe { - let Self { sink1, sink2 } = self.get_unchecked_mut(); - (Pin::new_unchecked(sink1), Pin::new_unchecked(sink2)) - } + #[project] + let Fanout { sink1, sink2 } = self.project(); + (sink1, sink2) } /// Consumes this combinator, returning the underlying sinks. @@ -65,41 +65,57 @@ impl Sink for Fanout { type Error = Si1::Error; + #[project] fn poll_ready( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_ready(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_ready(cx)?.is_ready(); + #[project] + let Fanout { sink1, sink2 } = self.project(); + + let sink1_ready = sink1.poll_ready(cx)?.is_ready(); + let sink2_ready = sink2.poll_ready(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; if ready { Poll::Ready(Ok(())) } else { Poll::Pending } } + #[project] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - self.as_mut().sink1().start_send(item.clone())?; - self.as_mut().sink2().start_send(item)?; + #[project] + let Fanout { sink1, sink2 } = self.project(); + + sink1.start_send(item.clone())?; + sink2.start_send(item)?; Ok(()) } + #[project] fn poll_flush( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_flush(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_flush(cx)?.is_ready(); + #[project] + let Fanout { sink1, sink2 } = self.project(); + + let sink1_ready = sink1.poll_flush(cx)?.is_ready(); + let sink2_ready = sink2.poll_flush(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; if ready { Poll::Ready(Ok(())) } else { Poll::Pending } } + #[project] fn poll_close( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let sink1_ready = self.as_mut().sink1().poll_close(cx)?.is_ready(); - let sink2_ready = self.as_mut().sink2().poll_close(cx)?.is_ready(); + #[project] + let Fanout { sink1, sink2 } = self.project(); + + let sink1_ready = sink1.poll_close(cx)?.is_ready(); + let sink2_ready = sink2.poll_close(cx)?.is_ready(); let ready = sink1_ready && sink2_ready; if ready { Poll::Ready(Ok(())) } else { Poll::Pending } } diff --git a/futures-util/src/sink/map_err.rs b/futures-util/src/sink/map_err.rs index e999bea78f..29994a79c4 100644 --- a/futures-util/src/sink/map_err.rs +++ b/futures-util/src/sink/map_err.rs @@ -2,51 +2,27 @@ use core::pin::Pin; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; use futures_sink::{Sink}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::pin_project; /// Sink for the [`sink_map_err`](super::SinkExt::sink_map_err) method. +#[pin_project] #[derive(Debug, Clone)] #[must_use = "sinks do nothing unless polled"] pub struct SinkMapErr { + #[pin] sink: Si, f: Option, } -impl Unpin for SinkMapErr {} - impl SinkMapErr { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: Option); - pub(super) fn new(sink: Si, f: F) -> SinkMapErr { SinkMapErr { sink, f: Some(f) } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); fn take_f(self: Pin<&mut Self>) -> F { - self.f().take().expect("polled MapErr after completion") + self.project().f.take().expect("polled MapErr after completion") } } @@ -60,28 +36,28 @@ impl Sink for SinkMapErr mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.as_mut().sink().poll_ready(cx).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.poll_ready(cx).map_err(|e| self.as_mut().take_f()(e)) } fn start_send( mut self: Pin<&mut Self>, item: Item, ) -> Result<(), Self::Error> { - self.as_mut().sink().start_send(item).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.start_send(item).map_err(|e| self.as_mut().take_f()(e)) } fn poll_flush( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.as_mut().sink().poll_flush(cx).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.poll_flush(cx).map_err(|e| self.as_mut().take_f()(e)) } fn poll_close( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.as_mut().sink().poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) + self.as_mut().project().sink.poll_close(cx).map_err(|e| self.as_mut().take_f()(e)) } } @@ -89,16 +65,7 @@ impl Sink for SinkMapErr impl Stream for SinkMapErr { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for SinkMapErr { diff --git a/futures-util/src/sink/with.rs b/futures-util/src/sink/with.rs index 5acac0176e..802123fc71 100644 --- a/futures-util/src/sink/with.rs +++ b/futures-util/src/sink/with.rs @@ -5,23 +5,20 @@ use futures_core::future::Future; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Sink for the [`with`](super::SinkExt::with) method. +#[pin_project] #[must_use = "sinks do nothing unless polled"] pub struct With { + #[pin] sink: Si, f: F, + #[pin] state: Option, _phantom: PhantomData Item>, } -impl Unpin for With -where - Si: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for With where Si: fmt::Debug, @@ -40,10 +37,6 @@ where Si: Sink, F: FnMut(U) -> Fut, Fut: Future, { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: F); - unsafe_pinned!(state: Option); - pub(super) fn new(sink: Si, f: F) -> Self where Fut: Future>, @@ -66,16 +59,7 @@ impl Stream for With { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl With @@ -84,40 +68,23 @@ impl With Fut: Future>, E: From, { - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); /// Completes the processing of previous item if any. + #[project] fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let item = match self.as_mut().state().as_pin_mut() { + #[project] + let With { mut state, sink, .. } = self.project(); + + let item = match state.as_mut().as_pin_mut() { None => return Poll::Ready(Ok(())), Some(fut) => ready!(fut.poll(cx))?, }; - self.as_mut().state().set(None); - self.as_mut().sink().start_send(item)?; + state.set(None); + sink.start_send(item)?; Poll::Ready(Ok(())) } } @@ -135,16 +102,20 @@ impl Sink for With cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_ready(cx)?); + ready!(self.project().sink.poll_ready(cx)?); Poll::Ready(Ok(())) } + #[project] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: U, ) -> Result<(), Self::Error> { - let future = (self.as_mut().f())(item); - self.as_mut().state().set(Some(future)); + #[project] + let With { mut state, f, .. } = self.project(); + + assert!(state.is_none()); + state.set(Some(f(item))); Ok(()) } @@ -153,7 +124,7 @@ impl Sink for With cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_flush(cx))?; + ready!(self.project().sink.poll_flush(cx)?); Poll::Ready(Ok(())) } @@ -162,7 +133,7 @@ impl Sink for With cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().poll(cx))?; - ready!(self.as_mut().sink().poll_close(cx))?; + ready!(self.project().sink.poll_close(cx)?); Poll::Ready(Ok(())) } } diff --git a/futures-util/src/sink/with_flat_map.rs b/futures-util/src/sink/with_flat_map.rs index 05ab77e7b1..f260a0bb4f 100644 --- a/futures-util/src/sink/with_flat_map.rs +++ b/futures-util/src/sink/with_flat_map.rs @@ -4,24 +4,21 @@ use core::pin::Pin; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Sink for the [`with_flat_map`](super::SinkExt::with_flat_map) method. +#[pin_project] #[must_use = "sinks do nothing unless polled"] pub struct WithFlatMap { + #[pin] sink: Si, f: F, + #[pin] stream: Option, buffer: Option, _marker: PhantomData, } -impl Unpin for WithFlatMap -where - Si: Unpin, - St: Unpin, -{} - impl fmt::Debug for WithFlatMap where Si: fmt::Debug, @@ -43,10 +40,6 @@ where F: FnMut(U) -> St, St: Stream>, { - unsafe_pinned!(sink: Si); - unsafe_unpinned!(f: F); - unsafe_pinned!(stream: Option); - pub(super) fn new(sink: Si, f: F) -> Self { WithFlatMap { sink, @@ -57,37 +50,15 @@ where } } - /// Get a shared reference to the inner sink. - pub fn get_ref(&self) -> &Si { - &self.sink - } - - /// Get a mutable reference to the inner sink. - pub fn get_mut(&mut self) -> &mut Si { - &mut self.sink - } - - /// Get a pinned mutable reference to the inner sink. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Si> { - self.sink() - } - - /// Consumes this combinator, returning the underlying sink. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> Si { - self.sink - } + delegate_access_inner!(sink, Si, ()); + #[project] fn try_empty_stream( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let WithFlatMap { sink, stream, buffer, .. } = - unsafe { self.get_unchecked_mut() }; - let mut sink = unsafe { Pin::new_unchecked(sink) }; - let mut stream = unsafe { Pin::new_unchecked(stream) }; + #[project] + let WithFlatMap { mut sink, mut stream, buffer, .. } = self.project(); if buffer.is_some() { ready!(sink.as_mut().poll_ready(cx))?; @@ -119,16 +90,7 @@ where { type Item = S::Item; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.sink().poll_next(cx) - } - - fn size_hint(&self) -> (usize, Option) { - self.sink.size_hint() - } + delegate_stream!(sink); } impl FusedStream for WithFlatMap @@ -157,13 +119,16 @@ where self.try_empty_stream(cx) } + #[project] fn start_send( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, item: U, ) -> Result<(), Self::Error> { - assert!(self.stream.is_none()); - let stream = (self.as_mut().f())(item); - self.stream().set(Some(stream)); + #[project] + let WithFlatMap { mut stream, f, .. } = self.project(); + + assert!(stream.is_none()); + stream.set(Some(f(item))); Ok(()) } @@ -172,7 +137,7 @@ where cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); - self.as_mut().sink().poll_flush(cx) + self.project().sink.poll_flush(cx) } fn poll_close( @@ -180,6 +145,6 @@ where cx: &mut Context<'_>, ) -> Poll> { ready!(self.as_mut().try_empty_stream(cx)?); - self.as_mut().sink().poll_close(cx) + self.project().sink.poll_close(cx) } } diff --git a/futures-util/src/stream/futures_ordered.rs b/futures-util/src/stream/futures_ordered.rs index 7cd7934a68..6dc07adf16 100644 --- a/futures-util/src/stream/futures_ordered.rs +++ b/futures-util/src/stream/futures_ordered.rs @@ -2,16 +2,18 @@ use crate::stream::{FuturesUnordered, StreamExt}; use futures_core::future::Future; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::iter::FromIterator; use core::pin::Pin; use alloc::collections::binary_heap::{BinaryHeap, PeekMut}; +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] struct OrderWrapper { + #[pin] data: T, // A future or a future's output index: usize, } @@ -37,21 +39,18 @@ impl Ord for OrderWrapper { } } -impl OrderWrapper { - unsafe_pinned!(data: T); -} - impl Future for OrderWrapper where T: Future { type Output = OrderWrapper; fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - self.as_mut().data().as_mut().poll(cx) - .map(|output| OrderWrapper { data: output, index: self.index }) + let index = self.index; + self.project().data.poll(cx) + .map(|output| OrderWrapper { data: output, index }) } } diff --git a/futures-util/src/stream/once.rs b/futures-util/src/stream/once.rs index 4f68b0cedd..21cd14b24e 100644 --- a/futures-util/src/stream/once.rs +++ b/futures-util/src/stream/once.rs @@ -2,7 +2,7 @@ use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Creates a stream of a single element. /// @@ -16,34 +16,39 @@ use pin_utils::unsafe_pinned; /// # }); /// ``` pub fn once(future: Fut) -> Once { - Once { future: Some(future) } + Once::new(future) } /// A stream which emits single element and then EOF. /// /// This stream will never block and is always ready. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Once { + #[pin] future: Option } -impl Unpin for Once {} - impl Once { - unsafe_pinned!(future: Option); + pub(crate) fn new(future: Fut) -> Self { + Self { future: Some(future) } + } } impl Stream for Once { type Item = Fut::Output; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let v = match self.as_mut().future().as_pin_mut() { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let Once { mut future } = self.project(); + let v = match future.as_mut().as_pin_mut() { Some(fut) => ready!(fut.poll(cx)), None => return Poll::Ready(None), }; - self.as_mut().future().set(None); + future.set(None); Poll::Ready(Some(v)) } diff --git a/futures-util/src/stream/select.rs b/futures-util/src/stream/select.rs index b5fb8133b2..36503e40d5 100644 --- a/futures-util/src/stream/select.rs +++ b/futures-util/src/stream/select.rs @@ -2,18 +2,20 @@ use crate::stream::{StreamExt, Fuse}; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; +use pin_project::{pin_project, project}; /// Stream for the [`select()`] function. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Select { + #[pin] stream1: Fuse, + #[pin] stream2: Fuse, flag: bool, } -impl Unpin for Select {} - /// This function will attempt to pull items from both streams. Each /// stream will be polled in a round-robin fashion, and whenever a stream is /// ready to yield an item that item is yielded. @@ -56,11 +58,11 @@ impl Select { /// /// Note that care must be taken to avoid tampering with the state of the /// stream which may otherwise confuse this combinator. + #[project] pub fn get_pin_mut(self: Pin<&mut Self>) -> (Pin<&mut St1>, Pin<&mut St2>) { - unsafe { - let Self { stream1, stream2, .. } = self.get_unchecked_mut(); - (Pin::new_unchecked(stream1).get_pin_mut(), Pin::new_unchecked(stream2).get_pin_mut()) - } + #[project] + let Select { stream1, stream2, .. } = self.project(); + (stream1.get_pin_mut(), stream2.get_pin_mut()) } /// Consumes this combinator, returning the underlying streams. @@ -87,14 +89,13 @@ impl Stream for Select { type Item = St1::Item; + #[project] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let Select { flag, stream1, stream2 } = - unsafe { self.get_unchecked_mut() }; - let stream1 = unsafe { Pin::new_unchecked(stream1) }; - let stream2 = unsafe { Pin::new_unchecked(stream2) }; + #[project] + let Select { flag, stream1, stream2 } = self.project(); if !*flag { poll_inner(flag, stream1, stream2, cx) diff --git a/futures-util/src/stream/stream/buffer_unordered.rs b/futures-util/src/stream/stream/buffer_unordered.rs index bea6e5b4dd..a822576f64 100644 --- a/futures-util/src/stream/stream/buffer_unordered.rs +++ b/futures-util/src/stream/stream/buffer_unordered.rs @@ -4,27 +4,24 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::fmt; use core::pin::Pin; /// Stream for the [`buffer_unordered`](super::StreamExt::buffer_unordered) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct BufferUnordered where St: Stream, { + #[pin] stream: Fuse, in_progress_queue: FuturesUnordered, max: usize, } -impl Unpin for BufferUnordered -where - St: Stream + Unpin, -{} - impl fmt::Debug for BufferUnordered where St: Stream + fmt::Debug, @@ -43,9 +40,6 @@ where St: Stream, St::Item: Future, { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(in_progress_queue: FuturesUnordered); - pub(super) fn new(stream: St, n: usize) -> BufferUnordered where St: Stream, @@ -58,37 +52,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for BufferUnordered @@ -98,27 +62,31 @@ where { type Item = ::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let BufferUnordered { mut stream, in_progress_queue, max } = self.project(); + // First up, try to spawn off as many futures as possible by filling up // our queue of futures. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut), + while in_progress_queue.len() < *max { + match stream.as_mut().poll_next(cx) { + Poll::Ready(Some(fut)) => in_progress_queue.push(fut), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - match self.as_mut().in_progress_queue().poll_next_unpin(cx) { + match in_progress_queue.poll_next_unpin(cx) { x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, Poll::Ready(None) => {} } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/stream/buffered.rs b/futures-util/src/stream/stream/buffered.rs index 2445a85c52..9dff01f0ad 100644 --- a/futures-util/src/stream/stream/buffered.rs +++ b/futures-util/src/stream/stream/buffered.rs @@ -4,28 +4,24 @@ use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::fmt; use core::pin::Pin; /// Stream for the [`buffered`](super::StreamExt::buffered) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Buffered where St: Stream, St::Item: Future, { + #[pin] stream: Fuse, in_progress_queue: FuturesOrdered, max: usize, } -impl Unpin for Buffered -where - St: Stream + Unpin, - St::Item: Future, -{} - impl fmt::Debug for Buffered where St: Stream + fmt::Debug, @@ -45,9 +41,6 @@ where St: Stream, St::Item: Future, { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(in_progress_queue: FuturesOrdered); - pub(super) fn new(stream: St, n: usize) -> Buffered { Buffered { stream: super::Fuse::new(stream), @@ -56,37 +49,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for Buffered @@ -96,27 +59,31 @@ where { type Item = ::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - // Try to spawn off as many futures as possible by filling up - // our in_progress_queue of futures. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut), + #[project] + let Buffered { mut stream, in_progress_queue, max } = self.project(); + + // First up, try to spawn off as many futures as possible by filling up + // our queue of futures. + while in_progress_queue.len() < *max { + match stream.as_mut().poll_next(cx) { + Poll::Ready(Some(fut)) => in_progress_queue.push(fut), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - let res = self.as_mut().in_progress_queue().poll_next_unpin(cx); + let res = in_progress_queue.poll_next_unpin(cx); if let Some(val) = ready!(res) { return Poll::Ready(Some(val)) } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/stream/catch_unwind.rs b/futures-util/src/stream/stream/catch_unwind.rs index 8d2dcf7663..1bb43b2ff0 100644 --- a/futures-util/src/stream/stream/catch_unwind.rs +++ b/futures-util/src/stream/stream/catch_unwind.rs @@ -1,45 +1,50 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use std::any::Any; use std::pin::Pin; use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe}; /// Stream for the [`catch_unwind`](super::StreamExt::catch_unwind) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct CatchUnwind { + #[pin] stream: St, caught_unwind: bool, } impl CatchUnwind { - unsafe_pinned!(stream: St); - unsafe_unpinned!(caught_unwind: bool); - pub(super) fn new(stream: St) -> CatchUnwind { CatchUnwind { stream, caught_unwind: false } } + + delegate_access_inner!(stream, St, ()); } impl Stream for CatchUnwind { type Item = Result>; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.caught_unwind { + #[project] + let CatchUnwind { stream, caught_unwind } = self.project(); + + if *caught_unwind { Poll::Ready(None) } else { let res = catch_unwind(AssertUnwindSafe(|| { - self.as_mut().stream().poll_next(cx) + stream.poll_next(cx) })); match res { Ok(poll) => poll.map(|opt| opt.map(Ok)), Err(e) => { - *self.as_mut().caught_unwind() = true; + *caught_unwind = true; Poll::Ready(Some(Err(e))) }, } diff --git a/futures-util/src/stream/stream/chain.rs b/futures-util/src/stream/stream/chain.rs index b2ada69c11..720903cb1f 100644 --- a/futures-util/src/stream/stream/chain.rs +++ b/futures-util/src/stream/stream/chain.rs @@ -1,13 +1,16 @@ use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Stream for the [`chain`](super::StreamExt::chain) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Chain { + #[pin] first: Option, + #[pin] second: St2, } @@ -16,9 +19,6 @@ impl Chain where St1: Stream, St2: Stream, { - unsafe_pinned!(first: Option); - unsafe_pinned!(second: St2); - pub(super) fn new(stream1: St1, stream2: St2) -> Chain { Chain { first: Some(stream1), @@ -42,17 +42,20 @@ where St1: Stream, { type Item = St1::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if let Some(first) = self.as_mut().first().as_pin_mut() { + #[project] + let Chain { mut first, second } = self.project(); + if let Some(first) = first.as_mut().as_pin_mut() { if let Some(item) = ready!(first.poll_next(cx)) { return Poll::Ready(Some(item)) } } - self.as_mut().first().set(None); - self.as_mut().second().poll_next(cx) + first.set(None); + second.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/chunks.rs b/futures-util/src/stream/stream/chunks.rs index b42d1d178d..d24c31c9cb 100644 --- a/futures-util/src/stream/stream/chunks.rs +++ b/futures-util/src/stream/stream/chunks.rs @@ -3,26 +3,23 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::mem; use core::pin::Pin; use alloc::vec::Vec; /// Stream for the [`chunks`](super::StreamExt::chunks) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Chunks { + #[pin] stream: Fuse, items: Vec, cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 } -impl Unpin for Chunks {} - impl Chunks where St: Stream { - unsafe_unpinned!(items: Vec); - unsafe_pinned!(stream: Fuse); - pub(super) fn new(stream: St, capacity: usize) -> Chunks { assert!(capacity > 0); @@ -33,70 +30,43 @@ impl Chunks where St: Stream { } } - fn take(mut self: Pin<&mut Self>) -> Vec { + fn take(self: Pin<&mut Self>) -> Vec { let cap = self.cap; - mem::replace(self.as_mut().items(), Vec::with_capacity(cap)) + mem::replace(self.project().items, Vec::with_capacity(cap)) } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for Chunks { type Item = Vec; + #[project] fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let Chunks { mut stream, items, cap } = self.as_mut().project(); loop { - match ready!(self.as_mut().stream().poll_next(cx)) { + match ready!(stream.as_mut().poll_next(cx)) { // Push the item into the buffer and check whether it is full. // If so, replace our buffer with a new and empty one and return // the full one. Some(item) => { - self.as_mut().items().push(item); - if self.items.len() >= self.cap { - return Poll::Ready(Some(self.as_mut().take())) + items.push(item); + if items.len() >= *cap { + return Poll::Ready(Some(self.take())) } } // Since the underlying stream ran out of values, return what we // have buffered, if we have anything. None => { - let last = if self.items.is_empty() { + let last = if items.is_empty() { None } else { - let full_buf = mem::replace(self.as_mut().items(), Vec::new()); + let full_buf = mem::replace(items, Vec::new()); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/collect.rs b/futures-util/src/stream/stream/collect.rs index 127a3f7d25..349e42d920 100644 --- a/futures-util/src/stream/stream/collect.rs +++ b/futures-util/src/stream/stream/collect.rs @@ -3,24 +3,21 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`collect`](super::StreamExt::collect) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Collect { + #[pin] stream: St, collection: C, } -impl Unpin for Collect {} - impl Collect { - unsafe_pinned!(stream: St); - unsafe_unpinned!(collection: C); - - fn finish(mut self: Pin<&mut Self>) -> C { - mem::replace(self.as_mut().collection(), Default::default()) + fn finish(self: Pin<&mut Self>) -> C { + mem::replace(self.project().collection, Default::default()) } pub(super) fn new(stream: St) -> Collect { @@ -46,11 +43,14 @@ where St: Stream, { type Output = C; + #[project] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let Collect { mut stream, collection } = self.as_mut().project(); loop { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => self.as_mut().collection().extend(Some(e)), - None => return Poll::Ready(self.as_mut().finish()), + match ready!(stream.as_mut().poll_next(cx)) { + Some(e) => collection.extend(Some(e)), + None => return Poll::Ready(self.finish()), } } } diff --git a/futures-util/src/stream/stream/concat.rs b/futures-util/src/stream/stream/concat.rs index 704efc79fd..647632b490 100644 --- a/futures-util/src/stream/stream/concat.rs +++ b/futures-util/src/stream/stream/concat.rs @@ -2,26 +2,23 @@ use core::pin::Pin; use futures_core::future::{Future, FusedFuture}; use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`concat`](super::StreamExt::concat) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Concat { + #[pin] stream: St, accum: Option, } -impl Unpin for Concat {} - impl Concat where St: Stream, St::Item: Extend<::Item> + IntoIterator + Default, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(accum: Option); - pub(super) fn new(stream: St) -> Concat { Concat { stream, @@ -37,16 +34,19 @@ where St: Stream, { type Output = St::Item; + #[project] fn poll( - mut self: Pin<&mut Self>, cx: &mut Context<'_> + self: Pin<&mut Self>, cx: &mut Context<'_> ) -> Poll { + #[project] + let Concat { mut stream, accum } = self.project(); + loop { - match ready!(self.as_mut().stream().poll_next(cx)) { + match ready!(stream.as_mut().poll_next(cx)) { None => { - return Poll::Ready(self.as_mut().accum().take().unwrap_or_default()) + return Poll::Ready(accum.take().unwrap_or_default()) } Some(e) => { - let accum = self.as_mut().accum(); if let Some(a) = accum { a.extend(e) } else { diff --git a/futures-util/src/stream/stream/enumerate.rs b/futures-util/src/stream/stream/enumerate.rs index 6366c8b7f3..477a0525e9 100644 --- a/futures-util/src/stream/stream/enumerate.rs +++ b/futures-util/src/stream/stream/enumerate.rs @@ -3,22 +3,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`enumerate`](super::StreamExt::enumerate) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Enumerate { + #[pin] stream: St, count: usize, } -impl Unpin for Enumerate {} - impl Enumerate { - unsafe_pinned!(stream: St); - unsafe_unpinned!(count: usize); - pub(super) fn new(stream: St) -> Enumerate { Enumerate { stream, @@ -26,37 +23,7 @@ impl Enumerate { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Enumerate { @@ -68,15 +35,19 @@ impl FusedStream for Enumerate { impl Stream for Enumerate { type Item = (usize, St::Item); + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match ready!(self.as_mut().stream().poll_next(cx)) { + #[project] + let Enumerate { stream, count } = self.project(); + + match ready!(stream.poll_next(cx)) { Some(item) => { - let count = self.count; - *self.as_mut().count() += 1; - Poll::Ready(Some((count, item))) + let prev_count = *count; + *count += 1; + Poll::Ready(Some((prev_count, item))) } None => Poll::Ready(None), } diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index 06335f1ee0..c04c84aaa7 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -5,25 +5,23 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; +use crate::fns::FnMut1; /// Stream for the [`filter`](super::StreamExt::filter) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Filter where St: Stream, { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, } -impl Unpin for Filter -where - St: Stream + Unpin, - Fut: Unpin, -{} - impl fmt::Debug for Filter where St: Stream + fmt::Debug, @@ -41,14 +39,9 @@ where impl Filter where St: Stream, - F: FnMut(&St::Item) -> Fut, + F: for<'a> FnMut1<&'a St::Item, Output=Fut>, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - pub(super) fn new(stream: St, f: F) -> Filter { Filter { stream, @@ -58,37 +51,7 @@ where St: Stream, } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Filter @@ -103,34 +66,31 @@ impl FusedStream for Filter impl Stream for Filter where St: Stream, - F: FnMut(&St::Item) -> Fut, + F: for<'a> FnMut1<&'a St::Item, Output=Fut>, Fut: Future, { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - loop { - if self.pending_fut.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); + #[project] + let Filter { mut stream, f, mut pending_fut, pending_item } = self.project(); + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + if ready!(fut.poll(cx)) { + break pending_item.take(); + } + *pending_item = None; + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + pending_fut.set(Some(f.call_mut(&item))); + *pending_item = Some(item); + } else { + break None; } - - let yield_item = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if yield_item { - return Poll::Ready(Some(item)); - } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/filter_map.rs b/futures-util/src/stream/stream/filter_map.rs index 532e6cad96..2d098ee5d6 100644 --- a/futures-util/src/stream/stream/filter_map.rs +++ b/futures-util/src/stream/stream/filter_map.rs @@ -5,22 +5,20 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; +use crate::fns::FnMut1; /// Stream for the [`filter_map`](super::StreamExt::filter_map) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct FilterMap { + #[pin] stream: St, f: F, + #[pin] pending: Option, } -impl Unpin for FilterMap -where - St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for FilterMap where St: fmt::Debug, @@ -39,50 +37,16 @@ impl FilterMap F: FnMut(St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending: Option); - pub(super) fn new(stream: St, f: F) -> FilterMap { FilterMap { stream, f, pending: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for FilterMap where St: Stream + FusedStream, - F: FnMut(St::Item) -> Fut, + F: FnMut1, Fut: Future>, { fn is_terminated(&self) -> bool { @@ -92,31 +56,34 @@ impl FusedStream for FilterMap impl Stream for FilterMap where St: Stream, - F: FnMut(St::Item) -> Fut, + F: FnMut1, Fut: Future>, { type Item = T; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - loop { - if self.pending.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(item); - self.as_mut().pending().set(Some(fut)); - } - - let item = ready!(self.as_mut().pending().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending().set(None); - if item.is_some() { - return Poll::Ready(item); + #[project] + let FilterMap { mut stream, f, mut pending } = self.project(); + Poll::Ready(loop { + if let Some(p) = pending.as_mut().as_pin_mut() { + // We have an item in progress, poll that until it's done + let item = ready!(p.poll(cx)); + pending.set(None); + if item.is_some() { + break item; + } + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + // No item in progress, but the stream is still going + pending.set(Some(f.call_mut(item))); + } else { + // The stream is done + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { @@ -134,7 +101,7 @@ impl Stream for FilterMap #[cfg(feature = "sink")] impl Sink for FilterMap where S: Stream + Sink, - F: FnMut(S::Item) -> Fut, + F: FnMut1, Fut: Future, { type Error = S::Error; diff --git a/futures-util/src/stream/stream/flat_map.rs b/futures-util/src/stream/stream/flat_map.rs deleted file mode 100644 index e92bb853e5..0000000000 --- a/futures-util/src/stream/stream/flat_map.rs +++ /dev/null @@ -1,169 +0,0 @@ -use super::Map; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`flat_map`](super::StreamExt::flat_map) method. -#[must_use = "streams do nothing unless polled"] -pub struct FlatMap { - stream: Map, - inner_stream: Option, -} - -impl Unpin for FlatMap {} - -impl fmt::Debug for FlatMap -where - St: fmt::Debug, - U: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlatMap") - .field("stream", &self.stream) - .field("inner_stream", &self.inner_stream) - .finish() - } -} - -impl FlatMap -where - St: Stream, - U: Stream, - F: FnMut(St::Item) -> U, -{ - unsafe_pinned!(stream: Map); - unsafe_pinned!(inner_stream: Option); - - pub(super) fn new(stream: St, f: F) -> FlatMap { - FlatMap { - stream: Map::new(stream, f), - inner_stream: None, - } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } -} - -impl FusedStream for FlatMap -where - St: FusedStream, - U: FusedStream, - F: FnMut(St::Item) -> U, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - && self - .inner_stream - .as_ref() - .map(FusedStream::is_terminated) - .unwrap_or(true) - } -} - -impl Stream for FlatMap -where - St: Stream, - U: Stream, - F: FnMut(St::Item) -> U, -{ - type Item = U::Item; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if let Some(inner_stream) = self.as_mut().inner_stream().as_pin_mut() { - let next = ready!(inner_stream.poll_next(cx)); - - if next.is_some() { - break Poll::Ready(next); - } else { - self.as_mut().inner_stream().set(None); - } - } - - let next_stream = ready!(self.as_mut().stream().poll_next(cx)); - - if next_stream.is_some() { - self.as_mut().inner_stream().set(next_stream); - } else { - break Poll::Ready(None); - } - } - } - - fn size_hint(&self) -> (usize, Option) { - let stream_size_hint = self.stream.size_hint(); - let no_stream_items_left = stream_size_hint.1 == Some(0); - - if let Some(inner_stream_size_hint) = self.inner_stream.as_ref().map(|st| st.size_hint()) { - ( - stream_size_hint - .0 - .checked_add(inner_stream_size_hint.0) - .unwrap_or(stream_size_hint.0), - if no_stream_items_left { - inner_stream_size_hint.1 - } else { - // Can't know upper bound because next items are `Stream`s - None - }, - ) - } else { - ( - stream_size_hint.0, - if no_stream_items_left { - Some(0) - } else { - // Can't know upper bound because next items are `Stream`s - None - }, - ) - } - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for FlatMap -where - St: Stream + Sink, - U: Stream, - F: FnMut(St::Item) -> U, -{ - type Error = St::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/stream/flatten.rs b/futures-util/src/stream/stream/flatten.rs index b19ffc036e..4db77e1326 100644 --- a/futures-util/src/stream/stream/flatten.rs +++ b/futures-util/src/stream/stream/flatten.rs @@ -3,77 +3,28 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Stream for the [`flatten`](super::StreamExt::flatten) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] -pub struct Flatten -where - St: Stream, -{ +pub struct Flatten { + #[pin] stream: St, - next: Option, -} - -impl Unpin for Flatten -where - St: Stream + Unpin, - St::Item: Unpin, -{ + #[pin] + next: Option, } -impl Flatten -where - St: Stream, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(next: Option); -} - -impl Flatten -where - St: Stream, - St::Item: Stream, -{ +impl Flatten { pub(super) fn new(stream: St) -> Self { Self { stream, next: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } -impl FusedStream for Flatten +impl FusedStream for Flatten where St: FusedStream, St::Item: Stream, @@ -83,34 +34,36 @@ where } } -impl Stream for Flatten +impl Stream for Flatten where St: Stream, St::Item: Stream, { type Item = ::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.next.is_none() { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => self.as_mut().next().set(Some(e)), - None => return Poll::Ready(None), + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let Flatten { mut stream, mut next } = self.project(); + Poll::Ready(loop { + if let Some(s) = next.as_mut().as_pin_mut() { + if let Some(item) = ready!(s.poll_next(cx)) { + break Some(item); + } else { + next.set(None); } - } - - if let Some(item) = ready!(self.as_mut().next().as_pin_mut().unwrap().poll_next(cx)) { - return Poll::Ready(Some(item)); + } else if let Some(s) = ready!(stream.as_mut().poll_next(cx)) { + next.set(Some(s)); } else { - self.as_mut().next().set(None); + break None; } - } + }) } } // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Flatten +impl Sink for Flatten where S: Stream + Sink, { diff --git a/futures-util/src/stream/stream/fold.rs b/futures-util/src/stream/stream/fold.rs index e92a72ed90..d4bec25311 100644 --- a/futures-util/src/stream/stream/fold.rs +++ b/futures-util/src/stream/stream/fold.rs @@ -3,19 +3,20 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`fold`](super::StreamExt::fold) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Fold { + #[pin] stream: St, f: F, accum: Option, + #[pin] future: Option, } -impl Unpin for Fold {} - impl fmt::Debug for Fold where St: fmt::Debug, @@ -36,11 +37,6 @@ where St: Stream, F: FnMut(T, St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_unpinned!(accum: Option); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F, t: T) -> Fold { Fold { stream, @@ -68,25 +64,27 @@ impl Future for Fold { type Output = T; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // we're currently processing a future to produce a new accum value - if self.accum.is_none() { - let accum = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - *self.as_mut().accum() = Some(accum); - self.as_mut().future().set(None); - } - - let item = ready!(self.as_mut().stream().poll_next(cx)); - let accum = self.as_mut().accum().take() - .expect("Fold polled after completion"); - - if let Some(e) = item { - let future = (self.as_mut().f())(accum, e); - self.as_mut().future().set(Some(future)); + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let Fold { mut stream, f, accum, mut future } = self.project(); + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new accum value + *accum = Some(ready!(fut.poll(cx))); + future.set(None); + } else if accum.is_some() { + // we're waiting on a new item from the stream + let res = ready!(stream.as_mut().poll_next(cx)); + let a = accum.take().unwrap(); + if let Some(item) = res { + future.set(Some(f(a, item))); + } else { + break a; + } } else { - return Poll::Ready(accum) + panic!("Fold polled after completion") } - } + }) } } diff --git a/futures-util/src/stream/stream/for_each.rs b/futures-util/src/stream/stream/for_each.rs index f8adcb2927..fb3f40fe3c 100644 --- a/futures-util/src/stream/stream/for_each.rs +++ b/futures-util/src/stream/stream/for_each.rs @@ -3,22 +3,19 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`for_each`](super::StreamExt::for_each) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ForEach { + #[pin] stream: St, f: F, + #[pin] future: Option, } -impl Unpin for ForEach -where - St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for ForEach where St: fmt::Debug, @@ -37,10 +34,6 @@ where St: Stream, F: FnMut(St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F) -> ForEach { ForEach { stream, @@ -67,22 +60,20 @@ impl Future for ForEach { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + let ForEach { mut stream, f, mut future } = self.project(); loop { - if let Some(future) = self.as_mut().future().as_pin_mut() { - ready!(future.poll(cx)); - } - self.as_mut().future().set(None); - - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => { - let future = (self.as_mut().f())(e); - self.as_mut().future().set(Some(future)); - } - None => { - return Poll::Ready(()); - } + if let Some(fut) = future.as_mut().as_pin_mut() { + ready!(fut.poll(cx)); + future.set(None); + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + future.set(Some(f(item))); + } else { + break; } } + Poll::Ready(()) } } diff --git a/futures-util/src/stream/stream/for_each_concurrent.rs b/futures-util/src/stream/stream/for_each_concurrent.rs index 18ca4bd34e..88ff2d39e5 100644 --- a/futures-util/src/stream/stream/for_each_concurrent.rs +++ b/futures-util/src/stream/stream/for_each_concurrent.rs @@ -5,23 +5,20 @@ use core::num::NonZeroUsize; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`for_each_concurrent`](super::StreamExt::for_each_concurrent) /// method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ForEachConcurrent { + #[pin] stream: Option, f: F, futures: FuturesUnordered, limit: Option, } -impl Unpin for ForEachConcurrent -where St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for ForEachConcurrent where St: fmt::Debug, @@ -41,11 +38,6 @@ where St: Stream, F: FnMut(St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: Option); - unsafe_unpinned!(f: F); - unsafe_unpinned!(futures: FuturesUnordered); - unsafe_unpinned!(limit: Option); - pub(super) fn new(stream: St, limit: Option, f: F) -> ForEachConcurrent { ForEachConcurrent { stream: Some(stream), @@ -74,16 +66,17 @@ impl Future for ForEachConcurrent { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + #[project] + let ForEachConcurrent { mut stream, f, futures, limit } = self.project(); loop { let mut made_progress_this_iter = false; - // Try and pull an item from the stream - let current_len = self.futures.len(); // Check if we've already created a number of futures greater than `limit` - if self.limit.map(|limit| limit.get() > current_len).unwrap_or(true) { + if limit.map(|limit| limit.get() > futures.len()).unwrap_or(true) { let mut stream_completed = false; - let elem = if let Some(stream) = self.as_mut().stream().as_pin_mut() { + let elem = if let Some(stream) = stream.as_mut().as_pin_mut() { match stream.poll_next(cx) { Poll::Ready(Some(elem)) => { made_progress_this_iter = true; @@ -99,18 +92,17 @@ impl Future for ForEachConcurrent None }; if stream_completed { - self.as_mut().stream().set(None); + stream.set(None); } if let Some(elem) = elem { - let next_future = (self.as_mut().f())(elem); - self.as_mut().futures().push(next_future); + futures.push(f(elem)); } } - match self.as_mut().futures().poll_next_unpin(cx) { + match futures.poll_next_unpin(cx) { Poll::Ready(Some(())) => made_progress_this_iter = true, Poll::Ready(None) => { - if self.stream.is_none() { + if stream.is_none() { return Poll::Ready(()) } }, diff --git a/futures-util/src/stream/stream/forward.rs b/futures-util/src/stream/stream/forward.rs index fd89625093..9776056b54 100644 --- a/futures-util/src/stream/stream/forward.rs +++ b/futures-util/src/stream/stream/forward.rs @@ -1,59 +1,34 @@ -use crate::stream::{StreamExt, Fuse}; +use crate::stream::Fuse; use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; -use futures_core::stream::{Stream, TryStream}; +use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -const INVALID_POLL: &str = "polled `Forward` after completion"; +use pin_project::{pin_project, project}; /// Future for the [`forward`](super::StreamExt::forward) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Forward { +pub struct Forward { + #[pin] sink: Option, + #[pin] stream: Fuse, - buffered_item: Option, + buffered_item: Option, } -impl Unpin for Forward {} - -impl Forward -where - Si: Sink, - St: TryStream + Stream, -{ - unsafe_pinned!(sink: Option); - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(buffered_item: Option); - - pub(super) fn new(stream: St, sink: Si) -> Self { +impl Forward { + pub(crate) fn new(stream: St, sink: Si) -> Self { Forward { sink: Some(sink), - stream: stream.fuse(), - buffered_item: None, + stream: Fuse::new(stream), + buffered_item: None, } } - - fn try_start_send( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - item: St::Ok, - ) -> Poll> { - debug_assert!(self.buffered_item.is_none()); - { - let mut sink = self.as_mut().sink().as_pin_mut().unwrap(); - if sink.as_mut().poll_ready(cx)?.is_ready() { - return Poll::Ready(sink.start_send(item)); - } - } - *self.as_mut().buffered_item() = Some(item); - Poll::Pending - } } -impl FusedFuture for Forward +impl FusedFuture for Forward where Si: Sink, St: Stream>, @@ -63,34 +38,41 @@ where } } -impl Future for Forward +impl Future for Forward where Si: Sink, St: Stream>, { type Output = Result<(), E>; + #[project] fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - // If we've got an item buffered already, we need to write it to the - // sink before we can do anything else - if let Some(item) = self.as_mut().buffered_item().take() { - ready!(self.as_mut().try_start_send(cx, item))?; - } + #[project] + let Forward { mut sink, mut stream, buffered_item } = self.project(); + let mut si = sink.as_mut().as_pin_mut().expect("polled `Forward` after completion"); loop { - match self.as_mut().stream().poll_next(cx)? { - Poll::Ready(Some(item)) => - ready!(self.as_mut().try_start_send(cx, item))?, + // If we've got an item buffered already, we need to write it to the + // sink before we can do anything else + if buffered_item.is_some() { + ready!(si.as_mut().poll_ready(cx))?; + si.as_mut().start_send(buffered_item.take().unwrap())?; + } + + match stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(item)) => { + *buffered_item = Some(item); + } Poll::Ready(None) => { - ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL).poll_close(cx))?; - self.as_mut().sink().set(None); + ready!(si.poll_close(cx))?; + sink.set(None); return Poll::Ready(Ok(())) } Poll::Pending => { - ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL).poll_flush(cx))?; + ready!(si.poll_flush(cx))?; return Poll::Pending } } diff --git a/futures-util/src/stream/stream/fuse.rs b/futures-util/src/stream/stream/fuse.rs index 9085dc553c..971fe60c29 100644 --- a/futures-util/src/stream/stream/fuse.rs +++ b/futures-util/src/stream/stream/fuse.rs @@ -3,22 +3,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`fuse`](super::StreamExt::fuse) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Fuse { + #[pin] stream: St, done: bool, } -impl Unpin for Fuse {} - impl Fuse { - unsafe_pinned!(stream: St); - unsafe_unpinned!(done: bool); - pub(super) fn new(stream: St) -> Fuse { Fuse { stream, done: false } } @@ -32,37 +29,7 @@ impl Fuse { self.done } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Fuse { @@ -74,17 +41,21 @@ impl FusedStream for Fuse { impl Stream for Fuse { type Item = S::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.done { + #[project] + let Fuse { stream, done } = self.project(); + + if *done { return Poll::Ready(None); } - let item = ready!(self.as_mut().stream().poll_next(cx)); + let item = ready!(stream.poll_next(cx)); if item.is_none() { - *self.as_mut().done() = true; + *done = true; } Poll::Ready(item) } diff --git a/futures-util/src/stream/stream/inspect.rs b/futures-util/src/stream/stream/inspect.rs deleted file mode 100644 index e34970ae65..0000000000 --- a/futures-util/src/stream/stream/inspect.rs +++ /dev/null @@ -1,119 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect`](super::StreamExt::inspect) method. -#[must_use = "streams do nothing unless polled"] -pub struct Inspect { - stream: St, - f: F, -} - -impl Unpin for Inspect {} - -impl fmt::Debug for Inspect -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Inspect") - .field("stream", &self.stream) - .finish() - } -} - -impl Inspect - where St: Stream, - F: FnMut(&St::Item), -{ - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - pub(super) fn new(stream: St, f: F) -> Inspect { - Inspect { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for Inspect - where St: FusedStream, - F: FnMut(&St::Item), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -// used by `TryStreamExt::{inspect_ok, inspect_err}` -#[inline] -pub(crate) fn inspect(x: T, mut f: F) -> T { - f(&x); - x -} - -impl Stream for Inspect - where St: Stream, - F: FnMut(&St::Item), -{ - type Item = St::Item; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .poll_next(cx) - .map(|opt| opt.map(|e| inspect(e, self.as_mut().f()))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for Inspect - where S: Stream + Sink, - F: FnMut(&S::Item), -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/stream/into_future.rs b/futures-util/src/stream/stream/into_future.rs index abae98c0c9..0d49384bea 100644 --- a/futures-util/src/stream/stream/into_future.rs +++ b/futures-util/src/stream/stream/into_future.rs @@ -3,7 +3,6 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::unsafe_pinned; /// Future for the [`into_future`](super::StreamExt::into_future) method. #[derive(Debug)] @@ -12,11 +11,7 @@ pub struct StreamFuture { stream: Option, } -impl Unpin for StreamFuture {} - impl StreamFuture { - unsafe_pinned!(stream: Option); - pub(super) fn new(stream: St) -> StreamFuture { StreamFuture { stream: Some(stream) } } @@ -57,7 +52,7 @@ impl StreamFuture { /// in order to return it to the caller of `Future::poll` if the stream yielded /// an element. pub fn get_pin_mut(self: Pin<&mut Self>) -> Option> { - self.stream().as_pin_mut() + Pin::get_mut(self).stream.as_mut().map(Pin::new) } /// Consumes this combinator, returning the underlying stream. diff --git a/futures-util/src/stream/stream/map.rs b/futures-util/src/stream/stream/map.rs index 81194342c4..755f53a7cb 100644 --- a/futures-util/src/stream/stream/map.rs +++ b/futures-util/src/stream/stream/map.rs @@ -4,17 +4,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; + +use crate::fns::FnMut1; /// Stream for the [`map`](super::StreamExt::map) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Map { + #[pin] stream: St, f: F, } -impl Unpin for Map {} - impl fmt::Debug for Map where St: fmt::Debug, @@ -26,73 +28,38 @@ where } } -impl Map - where St: Stream, - F: FnMut(St::Item) -> T, -{ - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - pub(super) fn new(stream: St, f: F) -> Map { +impl Map { + pub(crate) fn new(stream: St, f: F) -> Map { Map { stream, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } -impl FusedStream for Map +impl FusedStream for Map where St: FusedStream, - F: FnMut(St::Item) -> T, + F: FnMut1, { fn is_terminated(&self) -> bool { self.stream.is_terminated() } } -impl Stream for Map +impl Stream for Map where St: Stream, - F: FnMut(St::Item) -> T, + F: FnMut1, { - type Item = T; + type Item = F::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .poll_next(cx) - .map(|opt| opt.map(|x| self.as_mut().f()(x))) + ) -> Poll> { + #[project] + let Map { stream, f } = self.project(); + let res = ready!(stream.poll_next(cx)); + Poll::Ready(res.map(|x| f.call_mut(x))) } fn size_hint(&self) -> (usize, Option) { @@ -102,11 +69,11 @@ impl Stream for Map // Forwarding impl of Sink from the underlying stream #[cfg(feature = "sink")] -impl Sink for Map - where S: Stream + Sink, - F: FnMut(S::Item) -> T, +impl Sink for Map + where St: Stream + Sink, + F: FnMut1, { - type Error = S::Error; + type Error = St::Error; delegate_sink!(stream, Item); } diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index a7e312b7b8..548dd83979 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -19,6 +19,8 @@ use futures_core::{ #[cfg(feature = "sink")] use futures_sink::Sink; +use crate::fns::{InspectFn, inspect_fn}; + mod chain; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::chain::Chain; @@ -53,9 +55,15 @@ pub use self::fold::Fold; #[cfg(feature = "sink")] mod forward; + #[cfg(feature = "sink")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::forward::Forward; +delegate_all!( + /// Future for the [`forward`](super::StreamExt::forward) method. + Forward( + forward::Forward + ): Debug + Future + Sink + Stream + FusedStream + New[|x: St, y: Si| forward::Forward::new(x, y)] + where St: TryStream +); mod for_each; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -69,18 +77,23 @@ mod into_future; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_future::StreamFuture; -mod inspect; -pub(crate) use self::inspect::inspect; // used by `TryStreamExt::{inspect_ok, inspect_err}` -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect::Inspect; +delegate_all!( + /// Stream for the [`inspect`](StreamExt::inspect) method. + Inspect( + map::Map> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] +); mod map; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::map::Map; -mod flat_map; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flat_map::FlatMap; +delegate_all!( + /// Stream for the [`flat_map`](StreamExt::flat_map) method. + FlatMap( + Flatten, U> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Flatten::new(Map::new(x, f))] +); mod next; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -550,7 +563,7 @@ pub trait StreamExt: Stream { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # }); /// ``` - fn flatten(self) -> Flatten + fn flatten(self) -> Flatten where Self::Item: Stream, Self: Sized, diff --git a/futures-util/src/stream/stream/peek.rs b/futures-util/src/stream/stream/peek.rs index 9272bafe90..fb0f8740d1 100644 --- a/futures-util/src/stream/stream/peek.rs +++ b/futures-util/src/stream/stream/peek.rs @@ -1,4 +1,3 @@ -use crate::future::Either; use crate::stream::{Fuse, StreamExt}; use core::fmt; use core::pin::Pin; @@ -7,26 +6,23 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// A `Stream` that implements a `peek` method. /// /// The `peek` method can be used to retrieve a reference /// to the next `Stream::Item` if available. A subsequent /// call to `poll` will return the owned item. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Peekable { + #[pin] stream: Fuse, peeked: Option, } -impl Unpin for Peekable {} - impl Peekable { - unsafe_pinned!(stream: Fuse); - unsafe_unpinned!(peeked: Option); - pub(super) fn new(stream: St) -> Peekable { Peekable { stream: stream.fuse(), @@ -34,37 +30,7 @@ impl Peekable { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); /// Produces a `Peek` future which retrieves a reference to the next item /// in the stream, or `None` if the underlying stream terminates. @@ -72,42 +38,27 @@ impl Peekable { Peek { inner: Some(self) } } - /// Attempt to poll the underlying stream, and return the mutable borrow - /// in case that is desirable to try for another time. - /// In case a peeking poll is successful, the reference to the next item - /// will be in the `Either::Right` variant; otherwise, the mutable borrow - /// will be in the `Either::Left` variant. - fn do_poll_peek( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Either, Option<&St::Item>> { - if self.peeked.is_some() { - let this: &Self = self.into_ref().get_ref(); - return Either::Right(this.peeked.as_ref()); - } - match self.as_mut().stream().poll_next(cx) { - Poll::Ready(None) => Either::Right(None), - Poll::Ready(Some(item)) => { - *self.as_mut().peeked() = Some(item); - let this: &Self = self.into_ref().get_ref(); - Either::Right(this.peeked.as_ref()) - } - _ => Either::Left(self), - } - } - /// Peek retrieves a reference to the next item in the stream. /// /// This method polls the underlying stream and return either a reference /// to the next item if the stream is ready or passes through any errors. + #[project] pub fn poll_peek( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match self.do_poll_peek(cx) { - Either::Left(_) => Poll::Pending, - Either::Right(poll) => Poll::Ready(poll), - } + #[project] + let Peekable { mut stream, peeked } = self.project(); + + Poll::Ready(loop { + if peeked.is_some() { + break peeked.as_ref(); + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + *peeked = Some(item); + } else { + break None; + } + }) } } @@ -120,11 +71,14 @@ impl FusedStream for Peekable { impl Stream for Peekable { type Item = S::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(item) = self.as_mut().peeked().take() { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let Peekable { stream, peeked } = self.project(); + if let Some(item) = peeked.take() { return Poll::Ready(Some(item)); } - self.as_mut().stream().poll_next(cx) + stream.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { @@ -151,13 +105,12 @@ where } /// Future for the [`Peekable::peek()`](self::Peekable::peek) function from [`Peekable`] +#[pin_project] #[must_use = "futures do nothing unless polled"] pub struct Peek<'a, St: Stream> { inner: Option>>, } -impl Unpin for Peek<'_, St> {} - impl fmt::Debug for Peek<'_, St> where St: Stream + fmt::Debug, @@ -181,15 +134,12 @@ where St: Stream, { type Output = Option<&'a St::Item>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if let Some(peekable) = self.inner.take() { - match peekable.do_poll_peek(cx) { - Either::Left(peekable) => { - self.inner = Some(peekable); - Poll::Pending - } - Either::Right(peek) => Poll::Ready(peek), - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.project().inner; + if let Some(peekable) = inner { + ready!(peekable.as_mut().poll_peek(cx)); + + inner.take().unwrap().poll_peek(cx) } else { panic!("Peek polled after completion") } diff --git a/futures-util/src/stream/stream/ready_chunks.rs b/futures-util/src/stream/stream/ready_chunks.rs index 9a762c9e1b..2152cb72f0 100644 --- a/futures-util/src/stream/stream/ready_chunks.rs +++ b/futures-util/src/stream/stream/ready_chunks.rs @@ -3,26 +3,23 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::mem; use core::pin::Pin; use alloc::vec::Vec; /// Stream for the [`ready_chunks`](super::StreamExt::ready_chunks) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct ReadyChunks { + #[pin] stream: Fuse, items: Vec, cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475 } -impl Unpin for ReadyChunks {} - impl ReadyChunks where St: Stream { - unsafe_unpinned!(items: Vec); - unsafe_pinned!(stream: Fuse); - pub(super) fn new(stream: St, capacity: usize) -> ReadyChunks { assert!(capacity > 0); @@ -33,60 +30,29 @@ impl ReadyChunks where St: Stream { } } - fn take(mut self: Pin<&mut Self>) -> Vec { - let cap = self.cap; - mem::replace(self.as_mut().items(), Vec::with_capacity(cap)) - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner() - } + delegate_access_inner!(stream, St, (.)); } impl Stream for ReadyChunks { type Item = Vec; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let ReadyChunks { items, cap, mut stream } = self.project(); + loop { - match self.as_mut().stream().poll_next(cx) { + match stream.as_mut().poll_next(cx) { // Flush all collected data if underlying stream doesn't contain // more ready values Poll::Pending => { - return if self.items.is_empty() { + return if items.is_empty() { Poll::Pending } else { - Poll::Ready(Some(self.as_mut().take())) + Poll::Ready(Some(mem::replace(items, Vec::with_capacity(*cap)))) } } @@ -94,19 +60,19 @@ impl Stream for ReadyChunks { // If so, replace our buffer with a new and empty one and return // the full one. Poll::Ready(Some(item)) => { - self.as_mut().items().push(item); - if self.items.len() >= self.cap { - return Poll::Ready(Some(self.as_mut().take())) + items.push(item); + if items.len() >= *cap { + return Poll::Ready(Some(mem::replace(items, Vec::with_capacity(*cap)))) } } // Since the underlying stream ran out of values, return what we // have buffered, if we have anything. Poll::Ready(None) => { - let last = if self.items.is_empty() { + let last = if items.is_empty() { None } else { - let full_buf = mem::replace(self.as_mut().items(), Vec::new()); + let full_buf = mem::replace(items, Vec::new()); Some(full_buf) }; diff --git a/futures-util/src/stream/stream/scan.rs b/futures-util/src/stream/stream/scan.rs index 4f937f4fd9..0cdfcbca40 100644 --- a/futures-util/src/stream/stream/scan.rs +++ b/futures-util/src/stream/stream/scan.rs @@ -5,7 +5,7 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; struct StateFn { state: S, @@ -13,15 +13,16 @@ struct StateFn { } /// Stream for the [`scan`](super::StreamExt::scan) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Scan { + #[pin] stream: St, state_f: Option>, + #[pin] future: Option, } -impl Unpin for Scan {} - impl fmt::Debug for Scan where St: Stream + fmt::Debug, @@ -40,10 +41,6 @@ where } impl Scan { - unsafe_pinned!(stream: St); - unsafe_unpinned!(state_f: Option>); - unsafe_pinned!(future: Option); - /// Checks if internal state is `None`. fn is_done_taking(&self) -> bool { self.state_f.is_none() @@ -67,37 +64,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for Scan @@ -108,29 +75,32 @@ where { type Item = B; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.is_done_taking() { return Poll::Ready(None); } - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let state_f = self.as_mut().state_f().as_mut().unwrap(); - let fut = (state_f.f)(&mut state_f.state, item); - self.as_mut().future().set(Some(fut)); - } - - let item = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - - if item.is_none() { - self.as_mut().state_f().take(); - } - - Poll::Ready(item) + #[project] + let Scan { mut stream, state_f, mut future } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.poll(cx)); + future.set(None); + + if item.is_none() { + *state_f = None; + } + + break item; + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + let state_f = state_f.as_mut().unwrap(); + future.set(Some((state_f.f)(&mut state_f.state, item))) + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/skip.rs b/futures-util/src/stream/stream/skip.rs index 0b7c632daf..c0f6611e25 100644 --- a/futures-util/src/stream/stream/skip.rs +++ b/futures-util/src/stream/stream/skip.rs @@ -3,22 +3,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`skip`](super::StreamExt::skip) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Skip { + #[pin] stream: St, remaining: usize, } -impl Unpin for Skip {} - impl Skip { - unsafe_pinned!(stream: St); - unsafe_unpinned!(remaining: usize); - pub(super) fn new(stream: St, n: usize) -> Skip { Skip { stream, @@ -26,37 +23,7 @@ impl Skip { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Skip { @@ -68,18 +35,22 @@ impl FusedStream for Skip { impl Stream for Skip { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - while self.remaining > 0 { - match ready!(self.as_mut().stream().poll_next(cx)) { - Some(_) => *self.as_mut().remaining() -= 1, - None => return Poll::Ready(None), + #[project] + let Skip { mut stream, remaining } = self.project(); + while *remaining > 0 { + if ready!(stream.as_mut().poll_next(cx)).is_some() { + *remaining -= 1; + } else { + return Poll::Ready(None); } } - self.as_mut().stream().poll_next(cx) + stream.poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/skip_while.rs b/futures-util/src/stream/stream/skip_while.rs index 666d9deabc..3d664f2732 100644 --- a/futures-util/src/stream/stream/skip_while.rs +++ b/futures-util/src/stream/stream/skip_while.rs @@ -5,20 +5,21 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`skip_while`](super::StreamExt::skip_while) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct SkipWhile where St: Stream { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, done_skipping: bool, } -impl Unpin for SkipWhile {} - impl fmt::Debug for SkipWhile where St: Stream + fmt::Debug, @@ -40,12 +41,6 @@ impl SkipWhile F: FnMut(&St::Item) -> Fut, Fut: Future, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_skipping: bool); - pub(super) fn new(stream: St, f: F) -> SkipWhile { SkipWhile { stream, @@ -56,37 +51,7 @@ impl SkipWhile } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for SkipWhile @@ -106,44 +71,48 @@ impl Stream for SkipWhile { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.done_skipping { - return self.as_mut().stream().poll_next(cx); - } - - loop { - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } + #[project] + let SkipWhile { mut stream, f, mut pending_fut, pending_item, done_skipping } = self.project(); - let skipped = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - let item = self.as_mut().pending_item().take().unwrap(); - self.as_mut().pending_fut().set(None); + if *done_skipping { + return stream.poll_next(cx); + } - if !skipped { - *self.as_mut().done_skipping() = true; - return Poll::Ready(Some(item)) + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + let skipped = ready!(fut.poll(cx)); + let item = pending_item.take(); + pending_fut.set(None); + if !skipped { + *done_skipping = true; + break item; + } + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { - let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; - let (_, upper) = self.stream.size_hint(); - let upper = match upper { - Some(x) => x.checked_add(pending_len), - None => None, - }; - (0, upper) // can't know a lower bound, due to the predicate + if self.done_skipping { + self.stream.size_hint() + } else { + let pending_len = if self.pending_item.is_some() { 1 } else { 0 }; + let (_, upper) = self.stream.size_hint(); + let upper = match upper { + Some(x) => x.checked_add(pending_len), + None => None, + }; + (0, upper) // can't know a lower bound, due to the predicate + } } } diff --git a/futures-util/src/stream/stream/take.rs b/futures-util/src/stream/stream/take.rs index 1109a4a0f0..4a68920c4e 100644 --- a/futures-util/src/stream/stream/take.rs +++ b/futures-util/src/stream/stream/take.rs @@ -4,22 +4,19 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`take`](super::StreamExt::take) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Take { + #[pin] stream: St, remaining: usize, } -impl Unpin for Take {} - impl Take { - unsafe_pinned!(stream: St); - unsafe_unpinned!(remaining: usize); - pub(super) fn new(stream: St, n: usize) -> Take { Take { stream, @@ -27,37 +24,7 @@ impl Take { } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for Take @@ -65,17 +32,21 @@ impl Stream for Take { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { if self.remaining == 0 { Poll::Ready(None) } else { - let next = ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, + #[project] + let Take { stream, remaining } = self.project(); + let next = ready!(stream.poll_next(cx)); + if next.is_some() { + *remaining -= 1; + } else { + *remaining = 0; } Poll::Ready(next) } diff --git a/futures-util/src/stream/stream/take_until.rs b/futures-util/src/stream/stream/take_until.rs index 6fbd605c74..3662620a58 100644 --- a/futures-util/src/stream/stream/take_until.rs +++ b/futures-util/src/stream/stream/take_until.rs @@ -5,16 +5,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; // FIXME: docs, tests /// Stream for the [`take_until`](super::StreamExt::take_until) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TakeUntil { + #[pin] stream: St, /// Contains the inner Future on start and None once the inner Future is resolved /// or taken out by the user. + #[pin] fut: Option, /// Contains fut's return value once fut is resolved fut_result: Option, @@ -22,8 +25,6 @@ pub struct TakeUntil { free: bool, } -impl Unpin for TakeUntil {} - impl fmt::Debug for TakeUntil where St: Stream + fmt::Debug, @@ -38,16 +39,6 @@ where } } -impl TakeUntil -where - St: Stream, - Fut: Future, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(fut: Option); - unsafe_pinned!(fut_result: Option); -} - impl TakeUntil where St: Stream, @@ -62,35 +53,7 @@ where } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream and the stopping - /// future, if it isn't resolved yet. - pub fn into_inner(self) -> (St, Option) { - (self.stream, self.fut) - } + delegate_access_inner!(stream, St, ()); /// Extract the stopping future out of the combinator. /// The future is returned only if it isn't resolved yet, ie. if the stream isn't stopped yet. @@ -158,22 +121,26 @@ where { type Item = St::Item; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if let Some(fut) = self.as_mut().fut().as_pin_mut() { - if let Poll::Ready(result) = fut.poll(cx) { - self.as_mut().fut().set(None); - self.as_mut().fut_result().set(Some(result)); + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let TakeUntil { stream, mut fut, fut_result, free } = self.project(); + + if let Some(f) = fut.as_mut().as_pin_mut() { + if let Poll::Ready(result) = f.poll(cx) { + fut.set(None); + *fut_result = Some(result); } } - if self.is_stopped() { + if !*free && fut.is_none() { // Future resolved, inner stream stopped Poll::Ready(None) } else { // Future either not resolved yet or taken out by the user - let item = ready!(self.as_mut().stream().poll_next(cx)); + let item = ready!(stream.poll_next(cx)); if item.is_none() { - self.as_mut().fut().set(None); + fut.set(None); } Poll::Ready(item) } diff --git a/futures-util/src/stream/stream/take_while.rs b/futures-util/src/stream/stream/take_while.rs index 68606ec263..d90061e8b2 100644 --- a/futures-util/src/stream/stream/take_while.rs +++ b/futures-util/src/stream/stream/take_while.rs @@ -5,20 +5,21 @@ use futures_core::stream::{Stream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`take_while`](super::StreamExt::take_while) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] -pub struct TakeWhile { +pub struct TakeWhile { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, done_taking: bool, } -impl Unpin for TakeWhile {} - impl fmt::Debug for TakeWhile where St: Stream + fmt::Debug, @@ -35,14 +36,6 @@ where } } -impl TakeWhile { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_taking: bool); -} - impl TakeWhile where St: Stream, F: FnMut(&St::Item) -> Fut, @@ -58,37 +51,7 @@ impl TakeWhile } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for TakeWhile @@ -98,34 +61,36 @@ impl Stream for TakeWhile { type Item = St::Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { if self.done_taking { return Poll::Ready(None); } - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let take = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if take { - Poll::Ready(Some(item)) - } else { - *self.as_mut().done_taking() = true; - Poll::Ready(None) - } + #[project] + let TakeWhile { mut stream, f, mut pending_fut, pending_item, done_taking } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + let take = ready!(fut.poll(cx)); + let item = pending_item.take(); + pending_fut.set(None); + if take { + break item; + } else { + *done_taking = true; + break None; + } + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/then.rs b/futures-util/src/stream/stream/then.rs index 39843b2504..d54512ecc0 100644 --- a/futures-util/src/stream/stream/then.rs +++ b/futures-util/src/stream/stream/then.rs @@ -5,18 +5,19 @@ use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`then`](super::StreamExt::then) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Then { + #[pin] stream: St, + #[pin] future: Option, f: F, } -impl Unpin for Then {} - impl fmt::Debug for Then where St: fmt::Debug, @@ -30,12 +31,6 @@ where } } -impl Then { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl Then where St: Stream, F: FnMut(St::Item) -> Fut, @@ -48,37 +43,7 @@ impl Then } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for Then @@ -98,22 +63,25 @@ impl Stream for Then { type Item = Fut::Output; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().poll_next(cx)) { - None => return Poll::Ready(None), - Some(e) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + #[project] + let Then { mut stream, f, mut future } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.poll(cx)); + future.set(None); + break Some(item); + } else if let Some(item) = ready!(stream.as_mut().poll_next(cx)) { + future.set(Some(f(item))); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/stream/zip.rs b/futures-util/src/stream/stream/zip.rs index f97ac17d35..6c148fb94a 100644 --- a/futures-util/src/stream/stream/zip.rs +++ b/futures-util/src/stream/stream/zip.rs @@ -3,33 +3,22 @@ use core::cmp; use core::pin::Pin; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`zip`](super::StreamExt::zip) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Zip { + #[pin] stream1: Fuse, + #[pin] stream2: Fuse, queued1: Option, queued2: Option, } -#[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4323 -impl Unpin for Zip -where - St1: Stream, - Fuse: Unpin, - St2: Stream, - Fuse: Unpin, -{} - impl Zip { - unsafe_pinned!(stream1: Fuse); - unsafe_pinned!(stream2: Fuse); - unsafe_unpinned!(queued1: Option); - unsafe_unpinned!(queued2: Option); - pub(super) fn new(stream1: St1, stream2: St2) -> Zip { Zip { stream1: stream1.fuse(), @@ -88,28 +77,31 @@ impl Stream for Zip { type Item = (St1::Item, St2::Item); + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.queued1.is_none() { - match self.as_mut().stream1().poll_next(cx) { - Poll::Ready(Some(item1)) => *self.as_mut().queued1() = Some(item1), + #[project] + let Zip { mut stream1, mut stream2, queued1, queued2 } = self.project(); + + if queued1.is_none() { + match stream1.as_mut().poll_next(cx) { + Poll::Ready(Some(item1)) => *queued1 = Some(item1), Poll::Ready(None) | Poll::Pending => {} } } - if self.queued2.is_none() { - match self.as_mut().stream2().poll_next(cx) { - Poll::Ready(Some(item2)) => *self.as_mut().queued2() = Some(item2), + if queued2.is_none() { + match stream2.as_mut().poll_next(cx) { + Poll::Ready(Some(item2)) => *queued2 = Some(item2), Poll::Ready(None) | Poll::Pending => {} } } - if self.queued1.is_some() && self.queued2.is_some() { - let pair = (self.as_mut().queued1().take().unwrap(), - self.as_mut().queued2().take().unwrap()); + if queued1.is_some() && queued2.is_some() { + let pair = (queued1.take().unwrap(), queued2.take().unwrap()); Poll::Ready(Some(pair)) - } else if self.stream1.is_done() || self.stream2.is_done() { + } else if stream1.is_done() || stream2.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/try_stream/and_then.rs b/futures-util/src/stream/try_stream/and_then.rs index 809c32a94b..563ed34dd0 100644 --- a/futures-util/src/stream/try_stream/and_then.rs +++ b/futures-util/src/stream/try_stream/and_then.rs @@ -5,18 +5,19 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`and_then`](super::TryStreamExt::and_then) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct AndThen { + #[pin] stream: St, + #[pin] future: Option, f: F, } -impl Unpin for AndThen {} - impl fmt::Debug for AndThen where St: fmt::Debug, @@ -30,12 +31,6 @@ where } } -impl AndThen { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl AndThen where St: TryStream, F: FnMut(St::Ok) -> Fut, @@ -45,37 +40,7 @@ impl AndThen Self { stream, future: None, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for AndThen @@ -85,22 +50,25 @@ impl Stream for AndThen { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - None => return Poll::Ready(None), - Some(e) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + #[project] + let AndThen { mut stream, mut future, f } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.try_poll(cx)); + future.set(None); + break Some(item); + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + future.set(Some(f(item))); + } else { + break None; + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/err_into.rs b/futures-util/src/stream/try_stream/err_into.rs deleted file mode 100644 index f5d92945f3..0000000000 --- a/futures-util/src/stream/try_stream/err_into.rs +++ /dev/null @@ -1,98 +0,0 @@ -use core::marker::PhantomData; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::unsafe_pinned; - -/// Stream for the [`err_into`](super::TryStreamExt::err_into) method. -#[derive(Debug)] -#[must_use = "streams do nothing unless polled"] -pub struct ErrInto { - stream: St, - _marker: PhantomData, -} - -impl Unpin for ErrInto {} - -impl ErrInto { - unsafe_pinned!(stream: St); - - pub(super) fn new(stream: St) -> Self { - ErrInto { stream, _marker: PhantomData } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for ErrInto -where - St: TryStream + FusedStream, - St::Error: Into, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for ErrInto -where - St: TryStream, - St::Error: Into, -{ - type Item = Result; - - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.stream().try_poll_next(cx) - .map(|res| res.map(|some| some.map_err(Into::into))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for ErrInto -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/inspect_err.rs b/futures-util/src/stream/try_stream/inspect_err.rs deleted file mode 100644 index 3c23ae0395..0000000000 --- a/futures-util/src/stream/try_stream/inspect_err.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::stream::stream::inspect; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. -#[must_use = "streams do nothing unless polled"] -pub struct InspectErr { - stream: St, - f: F, -} - -impl Unpin for InspectErr {} - -impl fmt::Debug for InspectErr -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InspectErr") - .field("stream", &self.stream) - .finish() - } -} - -impl InspectErr { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); -} - -impl InspectErr -where - St: TryStream, - F: FnMut(&St::Error), -{ - pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for InspectErr -where - St: TryStream + FusedStream, - F: FnMut(&St::Error), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for InspectErr -where - St: TryStream, - F: FnMut(&St::Error), -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map_err(|e| inspect(e, self.as_mut().f())))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for InspectErr -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/inspect_ok.rs b/futures-util/src/stream/try_stream/inspect_ok.rs deleted file mode 100644 index 89fb459be9..0000000000 --- a/futures-util/src/stream/try_stream/inspect_ok.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::stream::stream::inspect; -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. -#[must_use = "streams do nothing unless polled"] -pub struct InspectOk { - stream: St, - f: F, -} - -impl Unpin for InspectOk {} - -impl fmt::Debug for InspectOk -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InspectOk") - .field("stream", &self.stream) - .finish() - } -} - -impl InspectOk { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); -} - -impl InspectOk -where - St: TryStream, - F: FnMut(&St::Ok), -{ - pub(super) fn new(stream: St, f: F) -> Self { - Self { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for InspectOk -where - St: TryStream + FusedStream, - F: FnMut(&St::Ok), -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for InspectOk -where - St: TryStream, - F: FnMut(&St::Ok), -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map(|e| inspect(e, self.as_mut().f())))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for InspectOk -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/into_stream.rs b/futures-util/src/stream/try_stream/into_stream.rs index b0fa07aa79..370a327943 100644 --- a/futures-util/src/stream/try_stream/into_stream.rs +++ b/futures-util/src/stream/try_stream/into_stream.rs @@ -3,54 +3,24 @@ use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::pin_project; /// Stream for the [`into_stream`](super::TryStreamExt::into_stream) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct IntoStream { + #[pin] stream: St, } impl IntoStream { - unsafe_pinned!(stream: St); - #[inline] pub(super) fn new(stream: St) -> Self { IntoStream { stream } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for IntoStream { @@ -67,7 +37,7 @@ impl Stream for IntoStream { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - self.stream().try_poll_next(cx) + self.project().stream.try_poll_next(cx) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/map_err.rs b/futures-util/src/stream/try_stream/map_err.rs deleted file mode 100644 index 1b98d6b4bc..0000000000 --- a/futures-util/src/stream/try_stream/map_err.rs +++ /dev/null @@ -1,112 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`map_err`](super::TryStreamExt::map_err) method. -#[must_use = "streams do nothing unless polled"] -pub struct MapErr { - stream: St, - f: F, -} - -impl Unpin for MapErr {} - -impl fmt::Debug for MapErr -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapErr") - .field("stream", &self.stream) - .finish() - } -} - -impl MapErr { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - /// Creates a new MapErr. - pub(super) fn new(stream: St, f: F) -> Self { - MapErr { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for MapErr -where - St: TryStream + FusedStream, - F: FnMut(St::Error) -> E, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for MapErr -where - St: TryStream, - F: FnMut(St::Error) -> E, -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map_err(|e| self.as_mut().f()(e)))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for MapErr -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/map_ok.rs b/futures-util/src/stream/try_stream/map_ok.rs deleted file mode 100644 index 19d01be459..0000000000 --- a/futures-util/src/stream/try_stream/map_ok.rs +++ /dev/null @@ -1,112 +0,0 @@ -use core::fmt; -use core::pin::Pin; -use futures_core::stream::{FusedStream, Stream, TryStream}; -use futures_core::task::{Context, Poll}; -#[cfg(feature = "sink")] -use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; - -/// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. -#[must_use = "streams do nothing unless polled"] -pub struct MapOk { - stream: St, - f: F, -} - -impl Unpin for MapOk {} - -impl fmt::Debug for MapOk -where - St: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapOk") - .field("stream", &self.stream) - .finish() - } -} - -impl MapOk { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - - /// Creates a new MapOk. - pub(super) fn new(stream: St, f: F) -> Self { - MapOk { stream, f } - } - - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } -} - -impl FusedStream for MapOk -where - St: TryStream + FusedStream, - F: FnMut(St::Ok) -> T, -{ - fn is_terminated(&self) -> bool { - self.stream.is_terminated() - } -} - -impl Stream for MapOk -where - St: TryStream, - F: FnMut(St::Ok) -> T, -{ - type Item = Result; - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.as_mut() - .stream() - .try_poll_next(cx) - .map(|opt| opt.map(|res| res.map(|x| self.as_mut().f()(x)))) - } - - fn size_hint(&self) -> (usize, Option) { - self.stream.size_hint() - } -} - -// Forwarding impl of Sink from the underlying stream -#[cfg(feature = "sink")] -impl Sink for MapOk -where - S: Sink, -{ - type Error = S::Error; - - delegate_sink!(stream, Item); -} diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index 45a57ed7d1..bb736484a8 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -11,34 +11,53 @@ use futures_core::{ stream::TryStream, task::{Context, Poll}, }; +use crate::fns::{ + InspectOkFn, inspect_ok_fn, InspectErrFn, inspect_err_fn, MapErrFn, map_err_fn, IntoFn, into_fn, MapOkFn, map_ok_fn, +}; +use crate::stream::{Map, Inspect}; mod and_then; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::and_then::AndThen; -mod err_into; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::err_into::ErrInto; - -mod inspect_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_ok::InspectOk; - -mod inspect_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::inspect_err::InspectErr; +delegate_all!( + /// Stream for the [`err_into`](super::TryStreamExt::err_into) method. + ErrInto( + MapErr> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] +); + +delegate_all!( + /// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. + InspectOk( + Inspect, InspectOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] +); + +delegate_all!( + /// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. + InspectErr( + Inspect, InspectErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] +); mod into_stream; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use self::into_stream::IntoStream; -mod map_ok; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_ok::MapOk; - -mod map_err; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::map_err::MapErr; +delegate_all!( + /// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. + MapOk( + Map, MapOkFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] +); + +delegate_all!( + /// Stream for the [`map_err`](super::TryStreamExt::map_err) method. + MapErr( + Map, MapErrFn> + ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] +); mod or_else; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 diff --git a/futures-util/src/stream/try_stream/or_else.rs b/futures-util/src/stream/try_stream/or_else.rs index 33310d1ce3..0bba0d0d53 100644 --- a/futures-util/src/stream/try_stream/or_else.rs +++ b/futures-util/src/stream/try_stream/or_else.rs @@ -5,18 +5,19 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`or_else`](super::TryStreamExt::or_else) method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct OrElse { + #[pin] stream: St, + #[pin] future: Option, f: F, } -impl Unpin for OrElse {} - impl fmt::Debug for OrElse where St: fmt::Debug, @@ -30,12 +31,6 @@ where } } -impl OrElse { - unsafe_pinned!(stream: St); - unsafe_pinned!(future: Option); - unsafe_unpinned!(f: F); -} - impl OrElse where St: TryStream, F: FnMut(St::Error) -> Fut, @@ -45,37 +40,7 @@ impl OrElse Self { stream, future: None, f } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for OrElse @@ -85,23 +50,29 @@ impl Stream for OrElse { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.future.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)) { - None => return Poll::Ready(None), - Some(Ok(e)) => return Poll::Ready(Some(Ok(e))), - Some(Err(e)) => e, - }; - let fut = (self.as_mut().f())(item); - self.as_mut().future().set(Some(fut)); - } - - let e = ready!(self.as_mut().future().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().future().set(None); - Poll::Ready(Some(e)) + #[project] + let OrElse { mut stream, mut future, f } = self.project(); + + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + let item = ready!(fut.try_poll(cx)); + future.set(None); + break Some(item); + } else { + match ready!(stream.as_mut().try_poll_next(cx)) { + Some(Ok(item)) => break Some(Ok(item)), + Some(Err(e)) => { + future.set(Some(f(e))); + }, + None => break None, + } + } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_buffer_unordered.rs b/futures-util/src/stream/try_stream/try_buffer_unordered.rs index d11e1b4bae..566868b7f1 100644 --- a/futures-util/src/stream/try_stream/try_buffer_unordered.rs +++ b/futures-util/src/stream/try_stream/try_buffer_unordered.rs @@ -5,32 +5,27 @@ use futures_core::stream::{Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; use core::pin::Pin; /// Stream for the /// [`try_buffer_unordered`](super::TryStreamExt::try_buffer_unordered) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct TryBufferUnordered where St: TryStream { + #[pin] stream: Fuse>, in_progress_queue: FuturesUnordered>, max: usize, } -impl Unpin for TryBufferUnordered - where St: TryStream + Unpin -{} - impl TryBufferUnordered where St: TryStream, St::Ok: TryFuture, { - unsafe_pinned!(stream: Fuse>); - unsafe_unpinned!(in_progress_queue: FuturesUnordered>); - pub(super) fn new(stream: St, n: usize) -> Self { TryBufferUnordered { stream: IntoStream::new(stream).fuse(), @@ -39,37 +34,7 @@ impl TryBufferUnordered } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - self.stream.get_ref().get_ref() - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - self.stream.get_mut().get_mut() - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream().get_pin_mut().get_pin_mut() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream.into_inner().into_inner() - } + delegate_access_inner!(stream, St, (. .)); } impl Stream for TryBufferUnordered @@ -78,27 +43,31 @@ impl Stream for TryBufferUnordered { type Item = Result<::Ok, St::Error>; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { + #[project] + let TryBufferUnordered { mut stream, in_progress_queue, max } = self.project(); + // First up, try to spawn off as many futures as possible by filling up // our queue of futures. Propagate errors from the stream immediately. - while self.in_progress_queue.len() < self.max { - match self.as_mut().stream().poll_next(cx)? { - Poll::Ready(Some(fut)) => self.as_mut().in_progress_queue().push(fut.into_future()), + while in_progress_queue.len() < *max { + match stream.as_mut().poll_next(cx)? { + Poll::Ready(Some(fut)) => in_progress_queue.push(fut.into_future()), Poll::Ready(None) | Poll::Pending => break, } } // Attempt to pull the next value from the in_progress_queue - match self.as_mut().in_progress_queue().poll_next_unpin(cx) { + match in_progress_queue.poll_next_unpin(cx) { x @ Poll::Pending | x @ Poll::Ready(Some(_)) => return x, Poll::Ready(None) => {} } // If more values are still coming from the stream, we're not done yet - if self.stream.is_done() { + if stream.is_done() { Poll::Ready(None) } else { Poll::Pending diff --git a/futures-util/src/stream/try_stream/try_collect.rs b/futures-util/src/stream/try_stream/try_collect.rs index d22e8e8543..3c9aee2a55 100644 --- a/futures-util/src/stream/try_stream/try_collect.rs +++ b/futures-util/src/stream/try_stream/try_collect.rs @@ -3,34 +3,27 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::{FusedStream, TryStream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_collect`](super::TryStreamExt::try_collect) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryCollect { + #[pin] stream: St, items: C, } impl TryCollect { - unsafe_pinned!(stream: St); - unsafe_unpinned!(items: C); - pub(super) fn new(s: St) -> TryCollect { TryCollect { stream: s, items: Default::default(), } } - - fn finish(self: Pin<&mut Self>) -> C { - mem::replace(self.items(), Default::default()) - } } -impl Unpin for TryCollect {} - impl FusedFuture for TryCollect where St: TryStream + FusedStream, @@ -48,15 +41,18 @@ where { type Output = Result; + #[project] fn poll( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll { - loop { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => self.as_mut().items().extend(Some(x)), - None => return Poll::Ready(Ok(self.as_mut().finish())), + #[project] + let TryCollect { mut stream, items } = self.project(); + Poll::Ready(Ok(loop { + match ready!(stream.as_mut().try_poll_next(cx)?) { + Some(x) => items.extend(Some(x)), + None => break mem::replace(items, Default::default()), } - } + })) } } diff --git a/futures-util/src/stream/try_stream/try_concat.rs b/futures-util/src/stream/try_stream/try_concat.rs index 395f166c6b..8c9710b217 100644 --- a/futures-util/src/stream/try_stream/try_concat.rs +++ b/futures-util/src/stream/try_stream/try_concat.rs @@ -2,26 +2,23 @@ use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_concat`](super::TryStreamExt::try_concat) method. +#[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryConcat { + #[pin] stream: St, accum: Option, } -impl Unpin for TryConcat {} - impl TryConcat where St: TryStream, St::Ok: Extend<::Item> + IntoIterator + Default, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(accum: Option); - pub(super) fn new(stream: St) -> TryConcat { TryConcat { stream, @@ -37,21 +34,20 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => { - let accum = self.as_mut().accum(); - if let Some(a) = accum { - a.extend(x) - } else { - *accum = Some(x) - } - }, - None => { - return Poll::Ready(Ok(self.as_mut().accum().take().unwrap_or_default())) + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryConcat { mut stream, accum } = self.project(); + Poll::Ready(Ok(loop { + if let Some(x) = ready!(stream.as_mut().try_poll_next(cx)?) { + if let Some(a) = accum { + a.extend(x) + } else { + *accum = Some(x) } + } else { + break accum.take().unwrap_or_default(); } - } + })) } } diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index 24a9c3275a..4694f7cdef 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -5,24 +5,23 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`try_filter`](super::TryStreamExt::try_filter) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TryFilter where St: TryStream { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, } -impl Unpin for TryFilter - where St: TryStream + Unpin, Fut: Unpin, -{} - impl fmt::Debug for TryFilter where St: TryStream + fmt::Debug, @@ -41,11 +40,6 @@ where impl TryFilter where St: TryStream { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - pub(super) fn new(stream: St, f: F) -> Self { TryFilter { stream, @@ -55,37 +49,7 @@ impl TryFilter } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilter @@ -105,29 +69,26 @@ impl Stream for TryFilter { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - loop { - if self.pending_fut.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => x, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let yield_item = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().poll(cx)); - self.as_mut().pending_fut().set(None); - let item = self.as_mut().pending_item().take().unwrap(); - - if yield_item { - return Poll::Ready(Some(Ok(item))); + #[project] + let TryFilter { mut stream, f, mut pending_fut, pending_item } = self.project(); + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + if ready!(fut.poll(cx)) { + break pending_item.take().map(Ok); + } + *pending_item = None; + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_filter_map.rs b/futures-util/src/stream/try_stream/try_filter_map.rs index ed7eeb227e..ba8e43a2cd 100644 --- a/futures-util/src/stream/try_stream/try_filter_map.rs +++ b/futures-util/src/stream/try_stream/try_filter_map.rs @@ -5,21 +5,20 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`try_filter_map`](super::TryStreamExt::try_filter_map) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TryFilterMap { + #[pin] stream: St, f: F, + #[pin] pending: Option, } -impl Unpin for TryFilterMap - where St: Unpin, Fut: Unpin, -{} - impl fmt::Debug for TryFilterMap where St: fmt::Debug, @@ -34,45 +33,11 @@ where } impl TryFilterMap { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(pending: Option); - pub(super) fn new(stream: St, f: F) -> Self { TryFilterMap { stream, f, pending: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFilterMap @@ -92,26 +57,29 @@ impl Stream for TryFilterMap { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - loop { - if self.pending.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(x) => x, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(item); - self.as_mut().pending().set(Some(fut)); - } - - let result = ready!(self.as_mut().pending().as_pin_mut().unwrap().try_poll(cx)); - self.as_mut().pending().set(None); - if let Some(x) = result? { - return Poll::Ready(Some(Ok(x))); + #[project] + let TryFilterMap { mut stream, f, mut pending } = self.project(); + Poll::Ready(loop { + if let Some(p) = pending.as_mut().as_pin_mut() { + // We have an item in progress, poll that until it's done + let item = ready!(p.try_poll(cx)?); + pending.set(None); + if item.is_some() { + break item.map(Ok); + } + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + // No item in progress, but the stream is still going + pending.set(Some(f(item))); + } else { + // The stream is done + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_flatten.rs b/futures-util/src/stream/try_stream/try_flatten.rs index 5f81b22b4c..a528639cae 100644 --- a/futures-util/src/stream/try_stream/try_flatten.rs +++ b/futures-util/src/stream/try_stream/try_flatten.rs @@ -3,34 +3,22 @@ use futures_core::stream::{FusedStream, Stream, TryStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::unsafe_pinned; +use pin_project::{pin_project, project}; /// Stream for the [`try_flatten`](super::TryStreamExt::try_flatten) method. +#[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct TryFlatten where St: TryStream, { + #[pin] stream: St, + #[pin] next: Option, } -impl Unpin for TryFlatten -where - St: TryStream + Unpin, - St::Ok: Unpin, -{ -} - -impl TryFlatten -where - St: TryStream, -{ - unsafe_pinned!(stream: St); - unsafe_pinned!(next: Option); -} - impl TryFlatten where St: TryStream, @@ -41,37 +29,7 @@ where Self { stream, next: None } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl FusedStream for TryFlatten @@ -93,27 +51,23 @@ where { type Item = Result<::Ok, ::Error>; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - if self.next.is_none() { - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => self.as_mut().next().set(Some(e)), - None => return Poll::Ready(None), + #[project] + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + #[project] + let TryFlatten { mut stream, mut next } = self.project(); + Poll::Ready(loop { + if let Some(s) = next.as_mut().as_pin_mut() { + if let Some(item) = ready!(s.try_poll_next(cx)?) { + break Some(Ok(item)); + } else { + next.set(None); } - } - - if let Some(item) = ready!(self - .as_mut() - .next() - .as_pin_mut() - .unwrap() - .try_poll_next(cx)?) - { - return Poll::Ready(Some(Ok(item))); + } else if let Some(s) = ready!(stream.as_mut().try_poll_next(cx)?) { + next.set(Some(s)); } else { - self.as_mut().next().set(None); + break None; } - } + }) } } diff --git a/futures-util/src/stream/try_stream/try_fold.rs b/futures-util/src/stream/try_stream/try_fold.rs index b8b8dc24f1..d85c1fe598 100644 --- a/futures-util/src/stream/try_stream/try_fold.rs +++ b/futures-util/src/stream/try_stream/try_fold.rs @@ -3,19 +3,20 @@ use core::pin::Pin; use futures_core::future::{FusedFuture, Future, TryFuture}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_fold`](super::TryStreamExt::try_fold) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryFold { + #[pin] stream: St, f: F, accum: Option, + #[pin] future: Option, } -impl Unpin for TryFold {} - impl fmt::Debug for TryFold where St: fmt::Debug, @@ -36,11 +37,6 @@ where St: TryStream, F: FnMut(T, St::Ok) -> Fut, Fut: TryFuture, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_unpinned!(accum: Option); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F, t: T) -> TryFold { TryFold { stream, @@ -68,43 +64,31 @@ impl Future for TryFold { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - // we're currently processing a future to produce a new accum value - if self.accum.is_none() { - let accum = match ready!( - self.as_mut().future().as_pin_mut() - .expect("TryFold polled after completion") - .try_poll(cx) - ) { - Ok(accum) => accum, - Err(e) => { - // Indicate that the future can no longer be polled. - self.as_mut().future().set(None); - return Poll::Ready(Err(e)); - } - }; - *self.as_mut().accum() = Some(accum); - self.as_mut().future().set(None); - } - - let item = match ready!(self.as_mut().stream().try_poll_next(cx)) { - Some(Ok(item)) => Some(item), - Some(Err(e)) => { - // Indicate that the future can no longer be polled. - *self.as_mut().accum() = None; - return Poll::Ready(Err(e)); + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryFold { mut stream, f, accum, mut future } = self.project(); + Poll::Ready(loop { + if let Some(fut) = future.as_mut().as_pin_mut() { + // we're currently processing a future to produce a new accum value + let res = ready!(fut.try_poll(cx)); + future.set(None); + match res { + Ok(a) => *accum = Some(a), + Err(e) => break Err(e), + } + } else if accum.is_some() { + // we're waiting on a new item from the stream + let res = ready!(stream.as_mut().try_poll_next(cx)); + let a = accum.take().unwrap(); + match res { + Some(Ok(item)) => future.set(Some(f(a, item))), + Some(Err(e)) => break Err(e), + None => break Ok(a), } - None => None, - }; - let accum = self.as_mut().accum().take().unwrap(); - - if let Some(e) = item { - let future = (self.as_mut().f())(accum, e); - self.as_mut().future().set(Some(future)); } else { - return Poll::Ready(Ok(accum)) + panic!("Fold polled after completion") } - } + }) } } diff --git a/futures-util/src/stream/try_stream/try_for_each.rs b/futures-util/src/stream/try_stream/try_for_each.rs index 2c71107646..5fc91df6d7 100644 --- a/futures-util/src/stream/try_stream/try_for_each.rs +++ b/futures-util/src/stream/try_stream/try_for_each.rs @@ -3,18 +3,19 @@ use core::pin::Pin; use futures_core::future::{Future, TryFuture}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the [`try_for_each`](super::TryStreamExt::try_for_each) method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryForEach { + #[pin] stream: St, f: F, + #[pin] future: Option, } -impl Unpin for TryForEach {} - impl fmt::Debug for TryForEach where St: fmt::Debug, @@ -33,10 +34,6 @@ where St: TryStream, F: FnMut(St::Ok) -> Fut, Fut: TryFuture, { - unsafe_pinned!(stream: St); - unsafe_unpinned!(f: F); - unsafe_pinned!(future: Option); - pub(super) fn new(stream: St, f: F) -> TryForEach { TryForEach { stream, @@ -53,20 +50,21 @@ impl Future for TryForEach { type Output = Result<(), St::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryForEach { mut stream, f, mut future } = self.project(); loop { - if let Some(future) = self.as_mut().future().as_pin_mut() { - ready!(future.try_poll(cx))?; - } - self.as_mut().future().set(None); - - match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => { - let future = (self.as_mut().f())(e); - self.as_mut().future().set(Some(future)); + if let Some(fut) = future.as_mut().as_pin_mut() { + ready!(fut.try_poll(cx))?; + future.set(None); + } else { + match ready!(stream.as_mut().try_poll_next(cx)?) { + Some(e) => future.set(Some(f(e))), + None => break, } - None => return Poll::Ready(Ok(())), } } + Poll::Ready(Ok(())) } } diff --git a/futures-util/src/stream/try_stream/try_for_each_concurrent.rs b/futures-util/src/stream/try_stream/try_for_each_concurrent.rs index 19c3e5b89a..87fd465fa8 100644 --- a/futures-util/src/stream/try_stream/try_for_each_concurrent.rs +++ b/futures-util/src/stream/try_stream/try_for_each_concurrent.rs @@ -6,24 +6,21 @@ use core::num::NonZeroUsize; use futures_core::future::{FusedFuture, Future}; use futures_core::stream::TryStream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Future for the /// [`try_for_each_concurrent`](super::TryStreamExt::try_for_each_concurrent) /// method. +#[pin_project] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct TryForEachConcurrent { + #[pin] stream: Option, f: F, futures: FuturesUnordered, limit: Option, } -impl Unpin for TryForEachConcurrent -where St: Unpin, - Fut: Unpin, -{} - impl fmt::Debug for TryForEachConcurrent where St: fmt::Debug, @@ -53,11 +50,6 @@ where St: TryStream, F: FnMut(St::Ok) -> Fut, Fut: Future>, { - unsafe_pinned!(stream: Option); - unsafe_unpinned!(f: F); - unsafe_unpinned!(futures: FuturesUnordered); - unsafe_unpinned!(limit: Option); - pub(super) fn new(stream: St, limit: Option, f: F) -> TryForEachConcurrent { TryForEachConcurrent { stream: Some(stream), @@ -76,15 +68,16 @@ impl Future for TryForEachConcurrent { type Output = Result<(), St::Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[project] + let TryForEachConcurrent { mut stream, f, futures, limit } = self.project(); loop { let mut made_progress_this_iter = false; - // Try and pull an item from the stream - let current_len = self.futures.len(); // Check if we've already created a number of futures greater than `limit` - if self.limit.map(|limit| limit.get() > current_len).unwrap_or(true) { - let poll_res = match self.as_mut().stream().as_pin_mut() { + if limit.map(|limit| limit.get() > futures.len()).unwrap_or(true) { + let poll_res = match stream.as_mut().as_pin_mut() { Some(stream) => stream.try_poll_next(cx), None => Poll::Ready(None), }; @@ -95,29 +88,28 @@ impl Future for TryForEachConcurrent Some(elem) }, Poll::Ready(None) => { - self.as_mut().stream().set(None); + stream.set(None); None } Poll::Pending => None, Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know // the future has completed. - self.as_mut().stream().set(None); - drop(mem::replace(self.as_mut().futures(), FuturesUnordered::new())); + stream.set(None); + drop(mem::replace(futures, FuturesUnordered::new())); return Poll::Ready(Err(e)); } }; if let Some(elem) = elem { - let next_future = (self.as_mut().f())(elem); - self.as_mut().futures().push(next_future); + futures.push(f(elem)); } } - match self.as_mut().futures().poll_next_unpin(cx) { + match futures.poll_next_unpin(cx) { Poll::Ready(Some(Ok(()))) => made_progress_this_iter = true, Poll::Ready(None) => { - if self.stream.is_none() { + if stream.is_none() { return Poll::Ready(Ok(())) } }, @@ -125,8 +117,8 @@ impl Future for TryForEachConcurrent Poll::Ready(Some(Err(e))) => { // Empty the stream and futures so that we know // the future has completed. - self.as_mut().stream().set(None); - drop(mem::replace(self.as_mut().futures(), FuturesUnordered::new())); + stream.set(None); + drop(mem::replace(futures, FuturesUnordered::new())); return Poll::Ready(Err(e)); } } diff --git a/futures-util/src/stream/try_stream/try_skip_while.rs b/futures-util/src/stream/try_stream/try_skip_while.rs index a3d6803a1b..624380f32d 100644 --- a/futures-util/src/stream/try_stream/try_skip_while.rs +++ b/futures-util/src/stream/try_stream/try_skip_while.rs @@ -5,21 +5,22 @@ use futures_core::stream::{Stream, TryStream, FusedStream}; use futures_core::task::{Context, Poll}; #[cfg(feature = "sink")] use futures_sink::Sink; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Stream for the [`try_skip_while`](super::TryStreamExt::try_skip_while) /// method. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TrySkipWhile where St: TryStream { + #[pin] stream: St, f: F, + #[pin] pending_fut: Option, pending_item: Option, done_skipping: bool, } -impl Unpin for TrySkipWhile {} - impl fmt::Debug for TrySkipWhile where St: TryStream + fmt::Debug, @@ -36,22 +37,11 @@ where } } -impl TrySkipWhile - where St: TryStream, -{ - unsafe_pinned!(stream: St); -} - impl TrySkipWhile where St: TryStream, F: FnMut(&St::Ok) -> Fut, Fut: TryFuture, { - unsafe_unpinned!(f: F); - unsafe_pinned!(pending_fut: Option); - unsafe_unpinned!(pending_item: Option); - unsafe_unpinned!(done_skipping: bool); - pub(super) fn new(stream: St, f: F) -> TrySkipWhile { TrySkipWhile { stream, @@ -62,37 +52,7 @@ impl TrySkipWhile } } - /// Acquires a reference to the underlying stream that this combinator is - /// pulling from. - pub fn get_ref(&self) -> &St { - &self.stream - } - - /// Acquires a mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_mut(&mut self) -> &mut St { - &mut self.stream - } - - /// Acquires a pinned mutable reference to the underlying stream that this - /// combinator is pulling from. - /// - /// Note that care must be taken to avoid tampering with the state of the - /// stream which may otherwise confuse this combinator. - pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut St> { - self.stream() - } - - /// Consumes this combinator, returning the underlying stream. - /// - /// Note that this may discard intermediate state of this combinator, so - /// care should be taken to avoid losing resources when this is called. - pub fn into_inner(self) -> St { - self.stream - } + delegate_access_inner!(stream, St, ()); } impl Stream for TrySkipWhile @@ -102,34 +62,34 @@ impl Stream for TrySkipWhile { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if self.done_skipping { - return self.as_mut().stream().try_poll_next(cx); - } + #[project] + let TrySkipWhile { mut stream, f, mut pending_fut, pending_item, done_skipping } = self.project(); - loop { - if self.pending_item.is_none() { - let item = match ready!(self.as_mut().stream().try_poll_next(cx)?) { - Some(e) => e, - None => return Poll::Ready(None), - }; - let fut = (self.as_mut().f())(&item); - self.as_mut().pending_fut().set(Some(fut)); - *self.as_mut().pending_item() = Some(item); - } - - let skipped = ready!(self.as_mut().pending_fut().as_pin_mut().unwrap().try_poll(cx)?); - let item = self.as_mut().pending_item().take().unwrap(); - self.as_mut().pending_fut().set(None); + if *done_skipping { + return stream.try_poll_next(cx); + } - if !skipped { - *self.as_mut().done_skipping() = true; - return Poll::Ready(Some(Ok(item))) + Poll::Ready(loop { + if let Some(fut) = pending_fut.as_mut().as_pin_mut() { + let skipped = ready!(fut.try_poll(cx)?); + let item = pending_item.take(); + pending_fut.set(None); + if !skipped { + *done_skipping = true; + break item.map(Ok); + } + } else if let Some(item) = ready!(stream.as_mut().try_poll_next(cx)?) { + pending_fut.set(Some(f(&item))); + *pending_item = Some(item); + } else { + break None; } - } + }) } fn size_hint(&self) -> (usize, Option) { diff --git a/futures-util/src/stream/try_stream/try_unfold.rs b/futures-util/src/stream/try_stream/try_unfold.rs index 6266274cd5..8da1248449 100644 --- a/futures-util/src/stream/try_stream/try_unfold.rs +++ b/futures-util/src/stream/try_stream/try_unfold.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::future::TryFuture; use futures_core::stream::Stream; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Creates a `TryStream` from a seed and a closure returning a `TryFuture`. /// @@ -67,15 +67,15 @@ where } /// Stream for the [`try_unfold`] function. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct TryUnfold { f: F, state: Option, + #[pin] fut: Option, } -impl Unpin for TryUnfold {} - impl fmt::Debug for TryUnfold where T: fmt::Debug, @@ -89,12 +89,6 @@ where } } -impl TryUnfold { - unsafe_unpinned!(f: F); - unsafe_unpinned!(state: Option); - unsafe_pinned!(fut: Option); -} - impl Stream for TryUnfold where F: FnMut(T) -> Fut, @@ -102,27 +96,30 @@ where { type Item = Result; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - if let Some(state) = self.as_mut().state().take() { - let fut = (self.as_mut().f())(state); - self.as_mut().fut().set(Some(fut)); + #[project] + let TryUnfold {f, state, mut fut } = self.project(); + + if let Some(state) = state.take() { + fut.set(Some(f(state))); } - match self.as_mut().fut().as_pin_mut() { + match fut.as_mut().as_pin_mut() { None => { // The future previously errored Poll::Ready(None) } - Some(fut) => { - let step = ready!(fut.try_poll(cx)); - self.as_mut().fut().set(None); + Some(future) => { + let step = ready!(future.try_poll(cx)); + fut.set(None); match step { Ok(Some((item, next_state))) => { - *self.as_mut().state() = Some(next_state); + *state = Some(next_state); Poll::Ready(Some(Ok(item))) } Ok(None) => Poll::Ready(None), diff --git a/futures-util/src/stream/unfold.rs b/futures-util/src/stream/unfold.rs index 3153f83711..0279571af8 100644 --- a/futures-util/src/stream/unfold.rs +++ b/futures-util/src/stream/unfold.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use futures_core::future::Future; use futures_core::stream::{FusedStream, Stream}; use futures_core::task::{Context, Poll}; -use pin_utils::{unsafe_pinned, unsafe_unpinned}; +use pin_project::{pin_project, project}; /// Creates a `Stream` from a seed and a closure returning a `Future`. /// @@ -56,15 +56,15 @@ pub fn unfold(init: T, f: F) -> Unfold } /// Stream for the [`unfold`] function. +#[pin_project] #[must_use = "streams do nothing unless polled"] pub struct Unfold { f: F, state: Option, + #[pin] fut: Option, } -impl Unpin for Unfold {} - impl fmt::Debug for Unfold where T: fmt::Debug, @@ -78,12 +78,6 @@ where } } -impl Unfold { - unsafe_unpinned!(f: F); - unsafe_unpinned!(state: Option); - unsafe_pinned!(fut: Option); -} - impl FusedStream for Unfold where F: FnMut(T) -> Fut, Fut: Future>, @@ -99,21 +93,24 @@ impl Stream for Unfold { type Item = Item; + #[project] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - if let Some(state) = self.as_mut().state().take() { - let fut = (self.as_mut().f())(state); - self.as_mut().fut().set(Some(fut)); + #[project] + let Unfold { state, f, mut fut } = self.project(); + + if let Some(state) = state.take() { + fut.set(Some(f(state))); } - let step = ready!(self.as_mut().fut().as_pin_mut() + let step = ready!(fut.as_mut().as_pin_mut() .expect("Unfold must not be polled after it returned `Poll::Ready(None)`").poll(cx)); - self.as_mut().fut().set(None); + fut.set(None); if let Some((item, next_state)) = step { - *self.as_mut().state() = Some(next_state); + *state = Some(next_state); Poll::Ready(Some(item)) } else { Poll::Ready(None) From b5a6a5b25f2aaf027eef949d3383cf9f1c6fd4ed Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Fri, 24 Apr 2020 21:32:28 +0100 Subject: [PATCH 2/3] Address review comments --- futures-util/src/future/future/map.rs | 71 +++++++++++++++---- futures-util/src/future/future/mod.rs | 25 ++++--- futures-util/src/future/try_future/mod.rs | 35 ++++----- .../src/future/try_future/try_flatten.rs | 2 +- .../src/future/try_future/try_flatten_err.rs | 2 +- futures-util/src/stream/stream/filter.rs | 4 +- futures-util/src/stream/stream/mod.rs | 20 ++++-- futures-util/src/stream/try_stream/mod.rs | 10 +-- .../src/stream/try_stream/try_filter.rs | 4 +- 9 files changed, 115 insertions(+), 58 deletions(-) diff --git a/futures-util/src/future/future/map.rs b/futures-util/src/future/future/map.rs index 046da2b77c..080f87109a 100644 --- a/futures-util/src/future/future/map.rs +++ b/futures-util/src/future/future/map.rs @@ -1,24 +1,39 @@ use core::pin::Pin; +use core::ptr; use futures_core::future::{FusedFuture, Future}; use futures_core::task::{Context, Poll}; use pin_project::{pin_project, project}; use crate::fns::FnOnce1; -/// Future for the [`map`](super::FutureExt::map) method. +/// Internal Map future #[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct Map { - #[pin] - future: Fut, - f: Option, +pub enum Map { + Incomplete { + #[pin] + future: Fut, + f: F, + }, + Complete, +} + +// Helper type to mark a `Map` as complete without running its destructor. +struct UnsafeMarkAsComplete(*mut Map); + +impl Drop for UnsafeMarkAsComplete { + fn drop(&mut self) { + unsafe { + ptr::write(self.0, Map::Complete); + } + } } impl Map { /// Creates a new Map. pub(crate) fn new(future: Fut, f: F) -> Map { - Map { future, f: Some(f) } + Map::Incomplete { future, f } } } @@ -26,7 +41,12 @@ impl FusedFuture for Map where Fut: Future, F: FnOnce1, { - fn is_terminated(&self) -> bool { self.f.is_none() } + fn is_terminated(&self) -> bool { + match self { + Map::Incomplete { .. } => false, + Map::Complete => true, + } + } } impl Future for Map @@ -36,13 +56,34 @@ impl Future for Map type Output = T; #[project] - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - #[project] - let Map { future, f } = self.project(); - let output = ready!(future.poll(cx)); - let f = f.take() - .expect("Map must not be polled after it returned `Poll::Ready`"); - - Poll::Ready(f.call_once(output)) + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { + // Store this pointer for later... + let self_ptr: *mut Self = self.as_mut().get_unchecked_mut(); + + match &mut *self_ptr { + Map::Incomplete { future, f } => { + let mut future = Pin::new_unchecked(future); + let output = match future.as_mut().poll(cx) { + Poll::Ready(x) => x, + Poll::Pending => return Poll::Pending, + }; + + // Here be dragons + let f = ptr::read(f); + { + // The ordering here is important, the call to `drop_in_place` must be + // last as it may panic. Other lines must not panic. + let _cleanup = UnsafeMarkAsComplete(self_ptr); + ptr::drop_in_place(future.get_unchecked_mut()); + }; + + // Phew, everything is back to normal, and we should be in the + // `Complete` state! + Poll::Ready(f.call_once(output)) + }, + Map::Complete => panic!("Map must not be polled after it returned `Poll::Ready`"), + } + } } } diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs index b87dae8464..c3e035bfaa 100644 --- a/futures-util/src/future/future/mod.rs +++ b/futures-util/src/future/future/mod.rs @@ -28,7 +28,7 @@ delegate_all!( /// Future for the [`flatten`](super::FutureExt::flatten) method. Flatten( flatten::Flatten::Output> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + ): Debug + Future + FusedFuture + New[|x: F| flatten::Flatten::new(x)] where F: Future ); @@ -36,55 +36,60 @@ delegate_all!( /// Stream for the [`flatten_stream`](FutureExt::flatten_stream) method. FlattenStream( flatten::Flatten::Output> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] + ): Debug + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)] where F: Future ); #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use fuse::Fuse; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use map::Map; + +delegate_all!( + /// Future for the [`flatten`](super::FutureExt::flatten) method. + Map( + map::Map + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, f)] +); delegate_all!( /// Stream for the [`into_stream`](FutureExt::into_stream) method. IntoStream( crate::stream::Once - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] + ): Debug + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)] ); delegate_all!( /// Future for the [`map_into`](FutureExt::map_into) combinator. MapInto( Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, into_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, into_fn())] ); delegate_all!( /// Future for the [`then`](FutureExt::then) method. Then( flatten::Flatten, Fut2> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] + ): Debug + Future + FusedFuture + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))] ); delegate_all!( /// Future for the [`inspect`](FutureExt::inspect) method. Inspect( map::Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))] ); delegate_all!( /// Future for the [`never_error`](super::FutureExt::never_error) combinator. NeverError( Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())] ); delegate_all!( /// Future for the [`unit_error`](super::FutureExt::unit_error) combinator. UnitError( Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| Map::new(x, ok_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())] ); #[cfg(feature = "std")] diff --git a/futures-util/src/future/try_future/mod.rs b/futures-util/src/future/try_future/mod.rs index 4f4a4cc1d6..bd1ab33d1f 100644 --- a/futures-util/src/future/try_future/mod.rs +++ b/futures-util/src/future/try_future/mod.rs @@ -31,21 +31,22 @@ delegate_all!( /// Future for the [`try_flatten`](TryFutureExt::try_flatten) method. TryFlatten( try_flatten::TryFlatten - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten::TryFlatten::new(x)] + ): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten::TryFlatten::new(x)] ); delegate_all!( /// Future for the [`try_flatten_err`](TryFutureExt::try_flatten_err) method. TryFlattenErr( try_flatten_err::TryFlattenErr - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] + ): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)] ); delegate_all!( /// Future for the [`try_flatten_stream`](TryFutureExt::try_flatten_stream) method. - TryFlattenStream( - try_flatten::TryFlatten - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + TryFlattenStream( + try_flatten::TryFlatten + ): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + where Fut: TryFuture ); #[cfg(feature = "sink")] @@ -53,49 +54,49 @@ delegate_all!( /// Sink for the [`flatten_sink`](TryFutureExt::flatten_sink) method. FlattenSink( try_flatten::TryFlatten - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] + ): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)] ); delegate_all!( /// Future for the [`and_then`](TryFutureExt::and_then) method. AndThen( TryFlatten, Fut2> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] + ): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))] ); delegate_all!( /// Future for the [`or_else`](TryFutureExt::or_else) method. OrElse( TryFlattenErr, Fut2> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] + ): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))] ); delegate_all!( /// Future for the [`err_into`](TryFutureExt::err_into) method. ErrInto( MapErr> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapErr::new(x, into_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| MapErr::new(x, into_fn())] ); delegate_all!( /// Future for the [`ok_into`](TryFutureExt::ok_into) method. OkInto( MapOk> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut| MapOk::new(x, into_fn())] + ): Debug + Future + FusedFuture + New[|x: Fut| MapOk::new(x, into_fn())] ); delegate_all!( /// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method. InspectOk( Inspect, InspectOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))] ); delegate_all!( /// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method. InspectErr( Inspect, InspectErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))] ); #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -105,28 +106,28 @@ delegate_all!( /// Future for the [`map_ok`](TryFutureExt::map_ok) method. MapOk( Map, MapOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))] ); delegate_all!( /// Future for the [`map_err`](TryFutureExt::map_err) method. MapErr( Map, MapErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))] ); delegate_all!( /// Future for the [`map_ok_or_else`](TryFutureExt::map_ok_or_else) method. MapOkOrElse( Map, MapOkOrElseFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))] ); delegate_all!( /// Future for the [`unwrap_or_else`](TryFutureExt::unwrap_or_else) method. UnwrapOrElse( Map, UnwrapOrElseFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] + ): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))] ); impl TryFutureExt for Fut {} @@ -526,7 +527,7 @@ pub trait TryFutureExt: TryFuture { /// assert_eq!(list, Ok(vec![17, 18, 19])); /// # }); /// ``` - fn try_flatten_stream(self) -> TryFlattenStream + fn try_flatten_stream(self) -> TryFlattenStream where Self::Ok: TryStream, Self: Sized, diff --git a/futures-util/src/future/try_future/try_flatten.rs b/futures-util/src/future/try_future/try_flatten.rs index 428377a196..661d3adf88 100644 --- a/futures-util/src/future/try_future/try_flatten.rs +++ b/futures-util/src/future/try_future/try_flatten.rs @@ -57,7 +57,7 @@ impl Future for TryFlatten self.set(TryFlatten::Empty); break output; }, - TryFlatten::Empty => return Poll::Pending, + TryFlatten::Empty => panic!("TryFlatten polled after completion"), } }) } diff --git a/futures-util/src/future/try_future/try_flatten_err.rs b/futures-util/src/future/try_future/try_flatten_err.rs index b4c54efb67..fbb413daac 100644 --- a/futures-util/src/future/try_future/try_flatten_err.rs +++ b/futures-util/src/future/try_future/try_flatten_err.rs @@ -54,7 +54,7 @@ impl Future for TryFlattenErr self.set(TryFlattenErr::Empty); break output; }, - TryFlattenErr::Empty => return Poll::Pending, + TryFlattenErr::Empty => panic!("TryFlattenErr polled after completion"), } }) } diff --git a/futures-util/src/stream/stream/filter.rs b/futures-util/src/stream/stream/filter.rs index c04c84aaa7..9d848ad717 100644 --- a/futures-util/src/stream/stream/filter.rs +++ b/futures-util/src/stream/stream/filter.rs @@ -80,7 +80,9 @@ impl Stream for Filter let Filter { mut stream, f, mut pending_fut, pending_item } = self.project(); Poll::Ready(loop { if let Some(fut) = pending_fut.as_mut().as_pin_mut() { - if ready!(fut.poll(cx)) { + let res = ready!(fut.poll(cx)); + pending_fut.set(None); + if res { break pending_item.take(); } *pending_item = None; diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs index 548dd83979..359bb2f222 100644 --- a/futures-util/src/stream/stream/mod.rs +++ b/futures-util/src/stream/stream/mod.rs @@ -46,8 +46,14 @@ mod filter_map; pub use self::filter_map::FilterMap; mod flatten; -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 -pub use self::flatten::Flatten; + +delegate_all!( + /// Stream for the [`inspect`](StreamExt::inspect) method. + Flatten( + flatten::Flatten + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| flatten::Flatten::new(x)] + where St: Stream +); mod fold; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 @@ -61,7 +67,7 @@ delegate_all!( /// Future for the [`forward`](super::StreamExt::forward) method. Forward( forward::Forward - ): Debug + Future + Sink + Stream + FusedStream + New[|x: St, y: Si| forward::Forward::new(x, y)] + ): Debug + Future + FusedFuture + New[|x: St, y: Si| forward::Forward::new(x, y)] where St: TryStream ); @@ -81,7 +87,7 @@ delegate_all!( /// Stream for the [`inspect`](StreamExt::inspect) method. Inspect( map::Map> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St, f: F| map::Map::new(x, inspect_fn(f))] ); mod map; @@ -91,8 +97,8 @@ pub use self::map::Map; delegate_all!( /// Stream for the [`flat_map`](StreamExt::flat_map) method. FlatMap( - Flatten, U> - ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Flatten::new(Map::new(x, f))] + flatten::Flatten, U> + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| flatten::Flatten::new(Map::new(x, f))] ); mod next; @@ -563,7 +569,7 @@ pub trait StreamExt: Stream { /// assert_eq!(output, vec![1, 2, 3, 4]); /// # }); /// ``` - fn flatten(self) -> Flatten + fn flatten(self) -> Flatten where Self::Item: Stream, Self: Sized, diff --git a/futures-util/src/stream/try_stream/mod.rs b/futures-util/src/stream/try_stream/mod.rs index bb736484a8..99d5a6d4c1 100644 --- a/futures-util/src/stream/try_stream/mod.rs +++ b/futures-util/src/stream/try_stream/mod.rs @@ -24,21 +24,21 @@ delegate_all!( /// Stream for the [`err_into`](super::TryStreamExt::err_into) method. ErrInto( MapErr> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (.)] + New[|x: St| MapErr::new(x, into_fn())] ); delegate_all!( /// Stream for the [`inspect_ok`](super::TryStreamExt::inspect_ok) method. InspectOk( Inspect, InspectOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_ok_fn(f))] ); delegate_all!( /// Stream for the [`inspect_err`](super::TryStreamExt::inspect_err) method. InspectErr( Inspect, InspectErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Inspect::new(IntoStream::new(x), inspect_err_fn(f))] ); mod into_stream; @@ -49,14 +49,14 @@ delegate_all!( /// Stream for the [`map_ok`](super::TryStreamExt::map_ok) method. MapOk( Map, MapOkFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_ok_fn(f))] ); delegate_all!( /// Stream for the [`map_err`](super::TryStreamExt::map_err) method. MapErr( Map, MapErrFn> - ): Debug + Future + FusedFuture + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] + ): Debug + Sink + Stream + FusedStream + AccessInner[St, (. .)] + New[|x: St, f: F| Map::new(IntoStream::new(x), map_err_fn(f))] ); mod or_else; diff --git a/futures-util/src/stream/try_stream/try_filter.rs b/futures-util/src/stream/try_stream/try_filter.rs index 4694f7cdef..310f99164d 100644 --- a/futures-util/src/stream/try_stream/try_filter.rs +++ b/futures-util/src/stream/try_stream/try_filter.rs @@ -78,7 +78,9 @@ impl Stream for TryFilter let TryFilter { mut stream, f, mut pending_fut, pending_item } = self.project(); Poll::Ready(loop { if let Some(fut) = pending_fut.as_mut().as_pin_mut() { - if ready!(fut.poll(cx)) { + let res = ready!(fut.poll(cx)); + pending_fut.set(None); + if res { break pending_item.take().map(Ok); } *pending_item = None; From dfc66739469a4fe849987a676c9b3be6011f1fb8 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Tue, 28 Apr 2020 21:13:29 +0100 Subject: [PATCH 3/3] Update futures-util/src/future/future/flatten.rs Co-Authored-By: Taiki Endo --- futures-util/src/future/future/flatten.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/future/future/flatten.rs b/futures-util/src/future/future/flatten.rs index 5e2ec686ea..f59464c5be 100644 --- a/futures-util/src/future/future/flatten.rs +++ b/futures-util/src/future/future/flatten.rs @@ -52,7 +52,7 @@ impl Future for Flatten self.set(Flatten::Empty); break output; }, - Flatten::Empty => return Poll::Pending, + Flatten::Empty => panic!("Flatten polled after completion"), } }) }