Skip to content

Commit

Permalink
Rollup merge of rust-lang#47193 - cramertj:result-opts, r=TimNN
Browse files Browse the repository at this point in the history
Add transpose conversions for nested 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.
  • Loading branch information
GuillaumeGomez committed Jan 20, 2018
2 parents fe811eb + c9ae249 commit 0e270fc
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/libcore/option.rs
Expand Up @@ -881,6 +881,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]
Expand Down
29 changes: 29 additions & 0 deletions src/libcore/result.rs
Expand Up @@ -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]
Expand Down
57 changes: 57 additions & 0 deletions 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))
}

0 comments on commit 0e270fc

Please sign in to comment.