diff --git a/Cargo.toml b/Cargo.toml index a20fd94a2b..97b76b0eb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,5 @@ log = { version = "0.3", default-features = false } [features] use_std = [] +never = [] default = ["use_std"] diff --git a/src/lib.rs b/src/lib.rs index eec24c8719..c0ff2c7199 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,7 @@ #![no_std] #![deny(missing_docs)] +#![cfg_attr(feature = "never", feature(never_type, default_type_parameter_fallback))] #[macro_use] #[cfg(feature = "use_std")] @@ -176,11 +177,19 @@ mod empty; mod failed; mod finished; mod lazy; +#[cfg(feature = "never")] +mod never; pub use done::{done, Done}; pub use empty::{empty, Empty}; pub use failed::{failed, Failed}; pub use finished::{finished, Finished}; pub use lazy::{lazy, Lazy}; +#[cfg(feature = "never")] +pub use never::{ + never, Never, + always_failed, AlwaysFailed, + always_finished, AlwaysFinished +}; // combinators mod and_then; diff --git a/src/never.rs b/src/never.rs new file mode 100644 index 0000000000..199b4a32b5 --- /dev/null +++ b/src/never.rs @@ -0,0 +1,99 @@ +use core::marker; + +use {Future, Poll, Async}; + +/// A future which is never resolved. +/// +/// This future can be created with the `never` function. +#[derive(Copy, Clone)] +pub struct Never { + _data: marker::PhantomData<(T, E)>, +} + +/// Creates a future which never resolves, representing a computation that never +/// finishes. +/// +/// The returned future will never resolve with a success but is still +/// susceptible to cancellation. That is, if a callback is scheduled on the +/// returned future, it is only run once the future is dropped (canceled). +pub fn never() -> Never { + Never { _data: marker::PhantomData } +} + +impl Future for Never { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll { + Ok(Async::NotReady) + } +} + +/// A future representing a finished but erroneous computation. +/// +/// Created by the `always_failed` function. +pub struct AlwaysFailed { + _t: marker::PhantomData, + e: Option, +} + +/// Creates a "leaf future" from an immediate value of a failed computation. +/// +/// The returned future is similar to `done` where it will immediately run a +/// scheduled callback with the provided value. +/// +/// # Examples +/// +/// ``` +///#![feature(default_type_parameter_fallback)] +/// use futures::*; +/// +/// let future_of_err_1 = always_failed(1); +/// ``` +pub fn always_failed(e: E) -> AlwaysFailed { + AlwaysFailed { _t: marker::PhantomData, e: Some(e) } +} + +impl Future for AlwaysFailed { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll { + Err(self.e.take().expect("cannot poll AlwaysFailed twice")) + } +} + +/// A future representing a finished successful computation. +/// +/// Created by the `always_finished` function. +pub struct AlwaysFinished { + t: Option, + _e: marker::PhantomData, +} + +/// Creates a "leaf future" from an immediate value of a finished and +/// successful computation. +/// +/// The returned future is similar to `done` where it will immediately run a +/// scheduled callback with the provided value. +/// +/// # Examples +/// +/// ``` +///#![feature(default_type_parameter_fallback)] +/// use futures::*; +/// +/// let future_of_1 = always_finished(1); +/// ``` +pub fn always_finished(t: T) -> AlwaysFinished { + AlwaysFinished { t: Some(t), _e: marker::PhantomData } +} + +impl Future for AlwaysFinished { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll { + Ok(Async::Ready(self.t.take().expect("cannot poll AlwaysFinished twice"))) + } +} diff --git a/tests/all.rs b/tests/all.rs index 405b9fd4c7..a6e1b2eb52 100644 --- a/tests/all.rs +++ b/tests/all.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "never", feature(never_type, default_type_parameter_fallback))] + extern crate futures; use std::sync::mpsc::{channel, TryRecvError}; @@ -79,6 +81,24 @@ fn test_empty() { assert_empty(|| empty().then(|a| a)); } +#[cfg(feature = "never")] +#[test] +fn test_never() { + assert_empty(|| never()); + assert_empty(|| never().select(never())); + assert_empty(|| never().join(never())); + assert_empty(|| never().join(f_ok(1))); + assert_empty(|| f_ok(1).join(never())); + assert_empty(|| never().or_else(move |_| never())); + assert_empty(|| never().and_then(move |_| never())); + assert_empty(|| f_err(1).or_else(move |_| never())); + assert_empty(|| f_ok(1).and_then(move |_| never())); + // TODO: Remove when `!` implements `std::ops::Add`` + assert_empty(|| never::().map(|a| a + 1)); + assert_empty(|| never::().map_err(|a| a + 1)); + assert_empty(|| never().then(|a| a)); +} + #[test] fn test_finished() { assert_done(|| finished(1), ok(1)); @@ -103,6 +123,18 @@ fn flatten() { assert_empty(|| empty::().map(finished).flatten()); } +#[cfg(feature = "never")] +#[test] +fn flatten_never() { + // TODO: Uncomment when `std::convert::From` is implemented for `i32` + //assert_done(|| always_finished(always_finished(1)).flatten(), ok(1)); + //assert_done(|| always_finished(always_failed(1)).flatten(), err(1)); + //assert_done(|| always_failed(1u32).map(always_finished).flatten(), err(1)); + //assert_done(|| always_finished(always_finished(1)).flatten(), ok(1)); + assert_empty(|| always_finished(never()).flatten()); + assert_empty(|| never().map(always_finished::).flatten()); +} + #[test] fn smoke_oneshot() { assert_done(|| {