about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTaylor Cramer <cramertj@google.com>2018-01-04 11:34:03 -0800
committerTaylor Cramer <cramertj@google.com>2018-01-10 17:42:47 -0800
commitc9ae249265c5da207ba0d8a2f8be95e58817e1dc (patch)
treec56ad0103df2c2fafe1eaaef4f7f53286d897f25
parent8e7a609e635b728eba65d471c985ab462dc4cfc7 (diff)
downloadrust-c9ae249265c5da207ba0d8a2f8be95e58817e1dc.tar.gz
rust-c9ae249265c5da207ba0d8a2f8be95e58817e1dc.zip
Add transpose conversions for Option and Result
These impls are useful when working with combinator
methods that expect an option or a result, but you
have a Result<Option<T>, E> instead of an Option<Result<T, E>>
or vice versa.
-rw-r--r--src/libcore/option.rs29
-rw-r--r--src/libcore/result.rs29
-rw-r--r--src/test/run-pass/result-opt-conversions.rs57
3 files changed, 115 insertions, 0 deletions
diff --git a/src/libcore/option.rs b/src/libcore/option.rs
index d8f3ec38cf3..76cea587038 100644
--- a/src/libcore/option.rs
+++ b/src/libcore/option.rs
@@ -884,6 +884,35 @@ impl<T: Default> Option<T> {
     }
 }
 
+impl<T, E> Option<Result<T, E>> {
+    /// Transposes an `Option` of a `Result` into a `Result` of an `Option`.
+    ///
+    /// `None` will be mapped to `Ok(None)`.
+    /// `Some(Ok(_))` and `Some(Err(_))` will be mapped to `Ok(Some(_))` and `Err(_)`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(transpose_result)]
+    ///
+    /// #[derive(Debug, Eq, PartialEq)]
+    /// struct SomeErr;
+    ///
+    /// let x: Result<Option<i32>, SomeErr> = Ok(Some(5));
+    /// let y: Option<Result<i32, SomeErr>> = Some(Ok(5));
+    /// assert_eq!(x, y.transpose());
+    /// ```
+    #[inline]
+    #[unstable(feature = "transpose_result", issue = "47338")]
+    pub fn transpose(self) -> Result<Option<T>, E> {
+        match self {
+            Some(Ok(x)) => Ok(Some(x)),
+            Some(Err(e)) => Err(e),
+            None => Ok(None),
+        }
+    }
+}
+
 // This is a separate function to reduce the code size of .expect() itself.
 #[inline(never)]
 #[cold]
diff --git a/src/libcore/result.rs b/src/libcore/result.rs
index 2ace3d2aee8..3801db94e15 100644
--- a/src/libcore/result.rs
+++ b/src/libcore/result.rs
@@ -909,6 +909,35 @@ impl<T: Default, E> Result<T, E> {
     }
 }
 
+impl<T, E> Result<Option<T>, E> {
+    /// Transposes a `Result` of an `Option` into an `Option` of a `Result`.
+    ///
+    /// `Ok(None)` will be mapped to `None`.
+    /// `Ok(Some(_))` and `Err(_)` will be mapped to `Some(Ok(_))` and `Some(Err(_))`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(transpose_result)]
+    ///
+    /// #[derive(Debug, Eq, PartialEq)]
+    /// struct SomeErr;
+    ///
+    /// let x: Result<Option<i32>, SomeErr> = Ok(Some(5));
+    /// let y: Option<Result<i32, SomeErr>> = Some(Ok(5));
+    /// assert_eq!(x.transpose(), y);
+    /// ```
+    #[inline]
+    #[unstable(feature = "transpose_result", issue = "47338")]
+    pub fn transpose(self) -> Option<Result<T, E>> {
+        match self {
+            Ok(Some(x)) => Some(Ok(x)),
+            Ok(None) => None,
+            Err(e) => Some(Err(e)),
+        }
+    }
+}
+
 // This is a separate function to reduce the code size of the methods
 #[inline(never)]
 #[cold]
diff --git a/src/test/run-pass/result-opt-conversions.rs b/src/test/run-pass/result-opt-conversions.rs
new file mode 100644
index 00000000000..0f6da002dda
--- /dev/null
+++ b/src/test/run-pass/result-opt-conversions.rs
@@ -0,0 +1,57 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(transpose_result)]
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+struct BadNumErr;
+
+fn try_num(x: i32) -> Result<i32, BadNumErr> {
+    if x <= 5 {
+        Ok(x + 1)
+    } else {
+        Err(BadNumErr)
+    }
+}
+
+type ResOpt = Result<Option<i32>, BadNumErr>;
+type OptRes = Option<Result<i32, BadNumErr>>;
+
+fn main() {
+    let mut x: ResOpt = Ok(Some(5));
+    let mut y: OptRes = Some(Ok(5));
+    assert_eq!(x, y.transpose());
+    assert_eq!(x.transpose(), y);
+
+    x = Ok(None);
+    y = None;
+    assert_eq!(x, y.transpose());
+    assert_eq!(x.transpose(), y);
+
+    x = Err(BadNumErr);
+    y = Some(Err(BadNumErr));
+    assert_eq!(x, y.transpose());
+    assert_eq!(x.transpose(), y);
+
+    let res: Result<Vec<i32>, BadNumErr> =
+        (0..10)
+            .map(|x| {
+                let y = try_num(x)?;
+                Ok(if y % 2 == 0 {
+                    Some(y - 1)
+                } else {
+                    None
+                })
+            })
+            .filter_map(Result::transpose)
+            .collect();
+
+    assert_eq!(res, Err(BadNumErr))
+}