Skip to content

Commit 6c4fc2f

Browse files
Merge #593
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 <[email protected]>
2 parents 2357d1a + 1a666a7 commit 6c4fc2f

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

src/either_or_both.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,33 @@ impl<A, B> EitherOrBoth<A, B> {
164164
}
165165
}
166166

167+
/// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
168+
/// Otherwise, returns the wrapped value for the present element, and the supplied
169+
/// value for the other. The first (`l`) argument is used for a missing `Left`
170+
/// value. The second (`r`) argument is used for a missing `Right` value.
171+
///
172+
/// Arguments passed to `or` are eagerly evaluated; if you are passing
173+
/// the result of a function call, it is recommended to use [`or_else`],
174+
/// which is lazily evaluated.
175+
///
176+
/// [`or_else`]: EitherOrBoth::or_else
177+
///
178+
/// # Examples
179+
///
180+
/// ```
181+
/// # use itertools::EitherOrBoth;
182+
/// assert_eq!(EitherOrBoth::Both("tree", 1).or("stone", 5), ("tree", 1));
183+
/// assert_eq!(EitherOrBoth::Left("tree").or("stone", 5), ("tree", 5));
184+
/// assert_eq!(EitherOrBoth::Right(1).or("stone", 5), ("stone", 1));
185+
/// ```
186+
pub fn or(self, l: A, r: B) -> (A, B) {
187+
match self {
188+
Left(inner_l) => (inner_l, r),
189+
Right(inner_r) => (l, inner_r),
190+
Both(inner_l, inner_r) => (inner_l, inner_r),
191+
}
192+
}
193+
167194
/// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
168195
/// Otherwise, returns the wrapped value for the present element, and the [`default`](Default::default)
169196
/// for the other.
@@ -178,6 +205,28 @@ impl<A, B> EitherOrBoth<A, B> {
178205
EitherOrBoth::Both(l, r) => (l, r),
179206
}
180207
}
208+
209+
/// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
210+
/// Otherwise, returns the wrapped value for the present element, and computes the
211+
/// missing value with the supplied closure. The first argument (`l`) is used for a
212+
/// missing `Left` value. The second argument (`r`) is used for a missing `Right` value.
213+
///
214+
/// # Examples
215+
///
216+
/// ```
217+
/// # use itertools::EitherOrBoth;
218+
/// let k = 10;
219+
/// assert_eq!(EitherOrBoth::Both("tree", 1).or_else(|| "stone", || 2 * k), ("tree", 1));
220+
/// assert_eq!(EitherOrBoth::Left("tree").or_else(|| "stone", || 2 * k), ("tree", 20));
221+
/// assert_eq!(EitherOrBoth::Right(1).or_else(|| "stone", || 2 * k), ("stone", 1));
222+
/// ```
223+
pub fn or_else<L: FnOnce() -> A, R: FnOnce() -> B>(self, l: L, r: R) -> (A, B) {
224+
match self {
225+
Left(inner_l) => (inner_l, r()),
226+
Right(inner_r) => (l(), inner_r),
227+
Both(inner_l, inner_r) => (inner_l, inner_r),
228+
}
229+
}
181230
}
182231

183232
impl<T> EitherOrBoth<T, T> {

0 commit comments

Comments
 (0)