diff --git a/src/prelude.rs b/src/prelude.rs index 645d7a2..f808da0 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -11,6 +11,8 @@ //! use async_std::prelude::*; //! ``` +use cfg_if::cfg_if; + #[doc(no_inline)] pub use crate::future::Future; #[doc(no_inline)] @@ -36,3 +38,13 @@ pub use crate::io::seek::SeekExt as _; pub use crate::io::write::WriteExt as _; #[doc(hidden)] pub use crate::stream::stream::StreamExt as _; + +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + #[doc(no_inline)] + pub use crate::stream::DoubleEndedStream; + + #[doc(no_inline)] + pub use crate::stream::ExactSizeStream; + } +} diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs new file mode 100644 index 0000000..ef23691 --- /dev/null +++ b/src/stream/exact_size_stream.rs @@ -0,0 +1,120 @@ +pub use crate::stream::Stream; + +/// A stream that knows its exact length. +/// +/// Many [`Stream`]s don't know how many times they will iterate, but some do. +/// If a stream knows how many times it can iterate, providing access to +/// that information can be useful. For example, if you want to iterate +/// backwards, a good start is to know where the end is. +/// +/// When implementing an `ExactSizeStream`, you must also implement +/// [`Stream`]. When doing so, the implementation of [`size_hint`] *must* +/// return the exact size of the stream. +/// +/// [`Stream`]: trait.Stream.html +/// [`size_hint`]: trait.Stream.html#method.size_hint +/// +/// The [`len`] method has a default implementation, so you usually shouldn't +/// implement it. However, you may be able to provide a more performant +/// implementation than the default, so overriding it in this case makes sense. +/// +/// [`len`]: #method.len +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// // a finite range knows exactly how many times it will iterate +/// let five = 0..5; +/// +/// assert_eq!(5, five.len()); +/// ``` +/// +/// In the [module level docs][moddocs], we implemented an [`Stream`], +/// `Counter`. Let's implement `ExactSizeStream` for it as well: +/// +/// [moddocs]: index.html +/// +/// ``` +/// # use std::task::{Context, Poll}; +/// # use std::pin::Pin; +/// # use async_std::prelude::*; +/// # struct Counter { +/// # count: usize, +/// # } +/// # impl Counter { +/// # fn new() -> Counter { +/// # Counter { count: 0 } +/// # } +/// # } +/// # impl Stream for Counter { +/// # type Item = usize; +/// # fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { +/// # self.count += 1; +/// # if self.count < 6 { +/// # Poll::Ready(Some(self.count)) +/// # } else { +/// # Poll::Ready(None) +/// # } +/// # } +/// # } +/// # fn main() { async_std::task::block_on(async { +/// # +/// impl ExactSizeStream for Counter { +/// // We can easily calculate the remaining number of iterations. +/// fn len(&self) -> usize { +/// 5 - self.count +/// } +/// } +/// +/// // And now we can use it! +/// +/// let counter = Counter::new(); +/// +/// assert_eq!(5, counter.len()); +/// # }); +/// # } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(any(feature = "unstable", feature = "docs"))] +pub trait ExactSizeStream: Stream { + /// Returns the exact number of times the stream will iterate. + /// + /// This method has a default implementation, so you usually should not + /// implement it directly. However, if you can provide a more efficient + /// implementation, you can do so. See the [trait-level] docs for an + /// example. + /// + /// This function has the same safety guarantees as the [`size_hint`] + /// function. + /// + /// [trait-level]: trait.ExactSizeStream.html + /// [`size_hint`]: trait.Stream.html#method.size_hint + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // a finite range knows exactly how many times it will iterate + /// let five = 0..5; + /// + /// assert_eq!(5, five.len()); + /// ``` + fn len(&self) -> usize { + let (lower, upper) = self.size_hint(); + // Note: This assertion is overly defensive, but it checks the invariant + // guaranteed by the trait. If this trait were rust-internal, + // we could use debug_assert!; assert_eq! will check all Rust user + // implementations too. + assert_eq!(upper, Some(lower)); + lower + } +} + +impl ExactSizeStream for &mut I { + fn len(&self) -> usize { + (**self).len() + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 6372d81..070983e 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -39,12 +39,14 @@ mod repeat; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream; + mod exact_size_stream; mod fused_stream; mod extend; mod from_stream; mod into_stream; pub use double_ended_stream::DoubleEndedStream; + pub use exact_size_stream::ExactSizeStream; pub use extend::Extend; pub use from_stream::FromStream; pub use into_stream::IntoStream;