Skip to content

Commit

Permalink
Merge #593
Browse files Browse the repository at this point in the history
593: EitherOrBoth: Add or and or_else methods to simplify getting default values r=phimuemue a=carl-anders

## Introducing `or` and `or_else` methods to `EitherOrBoth`
This feature reduces the amount of code required to get specific default values when using EitherOrBoth.
An example of the current way of using `zip_longest` with custom default values:
```rust
let a = (0..=4).into_iter();
let b = (0..=2).into_iter();
let c = a.zip_longest(b).map(|e| match e {
    EitherOrBoth::Both(l, r) => (l, r),
    EitherOrBoth::Left(l) => (l, 2),
    EitherOrBoth::Right(r) => (4, r),
});
// c will now contain an iterator with (0,0), (1,1), (2,2), (3,2), (4,2).
```
An example with the proposed `or` method:
```rust
let a = (0..=4).into_iter();
let b = (0..=2).into_iter();
let c = a.zip_longest(b).map(|e| e.or(4, 2));
// c will now contain an iterator with (0,0), (1,1), (2,2), (3,2), (4,2).
```
I have also included the `or_else` method which does the same but with closures.

## Contribute questions
> Include tests for your new feature, preferably a QuickCheck test

There are no tests for the other `EitherOrBoth` methods, so I was unsure how to place them. However, I have added **DocTest**'s to both methods. These examples allows for easier to understand documentation, and also tests the validity of the methods.

> For new features, please first consider filing a PR to rust-lang/rust

The EitherOrBoth struct does not exist in rust std library.

## Concerns

The naming is slightly inconsistent when compared to rust's `std::Option`, considering the naming from there the added methods should be named `unwrap_or` and `unwrap_or_else`.
However, this would then become inconsistent with the existing method `EitherOrBoth::or_default`. Which is why I went with the chosen names.
I can change the method names if needed, but then we should consider changing `or_default` too.

## P.S.
The `CHANGELOG.md` file has the text `Add EitherOrBoth::or_default (#583)`. This number is wrong, it should be #538.


Co-authored-by: Carl Andersson <carl@carlandersson.net>
  • Loading branch information
bors[bot] and carl-anders committed Dec 29, 2021
2 parents 2357d1a + 1a666a7 commit 6c4fc2f
Showing 1 changed file with 49 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/either_or_both.rs
Expand Up @@ -164,6 +164,33 @@ impl<A, B> EitherOrBoth<A, B> {
}
}

/// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
/// Otherwise, returns the wrapped value for the present element, and the supplied
/// value for the other. The first (`l`) argument is used for a missing `Left`
/// value. The second (`r`) argument is used for a missing `Right` value.
///
/// Arguments passed to `or` are eagerly evaluated; if you are passing
/// the result of a function call, it is recommended to use [`or_else`],
/// which is lazily evaluated.
///
/// [`or_else`]: EitherOrBoth::or_else
///
/// # Examples
///
/// ```
/// # use itertools::EitherOrBoth;
/// assert_eq!(EitherOrBoth::Both("tree", 1).or("stone", 5), ("tree", 1));
/// assert_eq!(EitherOrBoth::Left("tree").or("stone", 5), ("tree", 5));
/// assert_eq!(EitherOrBoth::Right(1).or("stone", 5), ("stone", 1));
/// ```
pub fn or(self, l: A, r: B) -> (A, B) {
match self {
Left(inner_l) => (inner_l, r),
Right(inner_r) => (l, inner_r),
Both(inner_l, inner_r) => (inner_l, inner_r),
}
}

/// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
/// Otherwise, returns the wrapped value for the present element, and the [`default`](Default::default)
/// for the other.
Expand All @@ -178,6 +205,28 @@ impl<A, B> EitherOrBoth<A, B> {
EitherOrBoth::Both(l, r) => (l, r),
}
}

/// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
/// Otherwise, returns the wrapped value for the present element, and computes the
/// missing value with the supplied closure. The first argument (`l`) is used for a
/// missing `Left` value. The second argument (`r`) is used for a missing `Right` value.
///
/// # Examples
///
/// ```
/// # use itertools::EitherOrBoth;
/// let k = 10;
/// assert_eq!(EitherOrBoth::Both("tree", 1).or_else(|| "stone", || 2 * k), ("tree", 1));
/// assert_eq!(EitherOrBoth::Left("tree").or_else(|| "stone", || 2 * k), ("tree", 20));
/// assert_eq!(EitherOrBoth::Right(1).or_else(|| "stone", || 2 * k), ("stone", 1));
/// ```
pub fn or_else<L: FnOnce() -> A, R: FnOnce() -> B>(self, l: L, r: R) -> (A, B) {
match self {
Left(inner_l) => (inner_l, r()),
Right(inner_r) => (l(), inner_r),
Both(inner_l, inner_r) => (inner_l, inner_r),
}
}
}

impl<T> EitherOrBoth<T, T> {
Expand Down

0 comments on commit 6c4fc2f

Please sign in to comment.