Skip to content

Commit

Permalink
Add transpose conversions for Option and Result
Browse files Browse the repository at this point in the history
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
cramertj committed Jan 11, 2018
1 parent 8e7a609 commit c9ae249
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
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
29 changes: 29 additions & 0 deletions src/libcore/result.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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 c9ae249

Please sign in to comment.