about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2016-05-03 13:01:54 +1200
committerNick Cameron <ncameron@mozilla.com>2016-08-18 16:51:56 +1200
commit683bcc02955f988ab2726f1b7708baa6c1e64b7b (patch)
tree3f1fed8e080478012a770431b01f240f494244c0 /src/libcore
parentaef6971ca96be5f04291420cc773b8bfacb8b36d (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.rs125
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(()),
+        }
+    }
+}