diff options
| author | Nick Cameron <ncameron@mozilla.com> | 2016-05-03 13:01:54 +1200 |
|---|---|---|
| committer | Nick Cameron <ncameron@mozilla.com> | 2016-08-18 16:51:56 +1200 |
| commit | 683bcc02955f988ab2726f1b7708baa6c1e64b7b (patch) | |
| tree | 3f1fed8e080478012a770431b01f240f494244c0 /src/libcore | |
| parent | aef6971ca96be5f04291420cc773b8bfacb8b36d (diff) | |
Use a Carrier trait with the `?` operator
Allows use with `Option` and custom `Result`-like types.
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/ops.rs | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 4ac1b8394f4..a869b3c4f98 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -75,6 +75,8 @@ use cmp::PartialOrd; use fmt; use marker::{Sized, Unsize}; +use result::Result::{self, Ok, Err}; +use option::Option::{self, Some, None}; /// The `Drop` trait is used to run some code when a value goes out of scope. /// This is sometimes called a 'destructor'. @@ -2150,3 +2152,126 @@ pub trait BoxPlace<Data: ?Sized> : Place<Data> { /// Creates a globally fresh place. fn make_place() -> Self; } + +/// A trait for types which have success and error states and are meant to work +/// with the question mark operator. +/// When the `?` operator is used with a value, whether the value is in the +/// success or error state is determined by calling `translate`. +/// +/// This trait is **very** experimental, it will probably be iterated on heavily +/// before it is stabilised. Implementors should expect change. Users of `?` +/// should not rely on any implementations of `Carrier` other than `Result`, +/// i.e., you should not expect `?` to continue to work with `Option`, etc. +#[unstable(feature = "question_mark_carrier", issue = "31436")] +pub trait Carrier { + /// The type of the value when computation succeeds. + type Success; + /// The type of the value when computation errors out. + type Error; + + /// Create a `Carrier` from a success value. + fn from_success(Self::Success) -> Self; + + /// Create a `Carrier` from an error value. + fn from_error(Self::Error) -> Self; + + /// Translate this `Carrier` to another implementation of `Carrier` with the + /// same associated types. + fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>; +} + +#[unstable(feature = "question_mark_carrier", issue = "31436")] +impl<U, V> Carrier for Result<U, V> { + type Success = U; + type Error = V; + + fn from_success(u: U) -> Result<U, V> { + Ok(u) + } + + fn from_error(e: V) -> Result<U, V> { + Err(e) + } + + fn translate<T>(self) -> T + where T: Carrier<Success=U, Error=V> + { + match self { + Ok(u) => T::from_success(u), + Err(e) => T::from_error(e), + } + } +} + +#[unstable(feature = "question_mark_carrier", issue = "31436")] +impl<U> Carrier for Option<U> { + type Success = U; + type Error = (); + + fn from_success(u: U) -> Option<U> { + Some(u) + } + + fn from_error(_: ()) -> Option<U> { + None + } + + fn translate<T>(self) -> T + where T: Carrier<Success=U, Error=()> + { + match self { + Some(u) => T::from_success(u), + None => T::from_error(()), + } + } +} + +// Implementing Carrier for bools means it's easy to write short-circuiting +// functions. E.g., +// ``` +// fn foo() -> bool { +// if !(f() || g()) { +// return false; +// } +// +// some_computation(); +// if h() { +// return false; +// } +// +// more_computation(); +// i() +// } +// ``` +// becomes +// ``` +// fn foo() -> bool { +// (f() || g())?; +// some_computation(); +// (!h())?; +// more_computation(); +// i() +// } +// ``` +#[unstable(feature = "question_mark_carrier", issue = "31436")] +impl Carrier for bool { + type Success = (); + type Error = (); + + fn from_success(_: ()) -> bool { + true + } + + fn from_error(_: ()) -> bool { + false + } + + fn translate<T>(self) -> T + where T: Carrier<Success=(), Error=()> + { + match self { + true => T::from_success(()), + false => T::from_error(()), + } + } +} |
