Skip to content

Commit

Permalink
Auto merge of #63177 - MOZGIII:find-result, r=Amanieu
Browse files Browse the repository at this point in the history
Add Iterator::try_find

I found a need for this fn, and created this PR.

Tracking issue: #63178

I did a fair amount of thinking about the function name, and settled on the current one.
I don't see other anything else that's non-trivial here, but I'm open for debate. I just want this functionality to be there.
It couples with the `collect` trick for collecting `Result<Vec<T>, E>` from `Iterator<Item = Result<T, E>>`.

UPD:

I've already looked at `fallible_iterator` crate, but I don't think it supports my use case.
The main problem is that I can't construct a failable iterator. I have a regular iterator, and I just need to apply a predicate that can fail via `find` method.

UPD: `fallible_iterator` would work, but it's not elegant cause I'd have to make a failable iterator by mapping iterator with `Result::Ok` first.
  • Loading branch information
bors committed Jan 2, 2020
2 parents 766fba3 + 5446cc9 commit 0a58f58
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/libcore/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,43 @@ pub trait Iterator {
self.try_fold((), check(f)).break_value()
}

/// Applies function to the elements of iterator and returns
/// the first non-none result or the first error.
///
/// # Examples
///
/// ```
/// #![feature(try_find)]
///
/// let a = ["1", "2", "lol", "NaN", "5"];
///
/// let is_my_num = |s: &str, search: i32| -> Result<bool, std::num::ParseIntError> {
/// Ok(s.parse::<i32>()? == search)
/// };
///
/// let result = a.iter().try_find(|&&s| is_my_num(s, 2));
/// assert_eq!(result, Ok(Some(&"2")));
///
/// let result = a.iter().try_find(|&&s| is_my_num(s, 5));
/// assert!(result.is_err());
/// ```
#[inline]
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
fn try_find<F, E, R>(&mut self, mut f: F) -> Result<Option<Self::Item>, E>
where
Self: Sized,
F: FnMut(&Self::Item) -> R,
R: Try<Ok = bool, Error = E>,
{
self.try_for_each(move |x| match f(&x).into_result() {
Ok(false) => LoopState::Continue(()),
Ok(true) => LoopState::Break(Ok(x)),
Err(x) => LoopState::Break(Err(x)),
})
.break_value()
.transpose()
}

/// Searches for an element in an iterator, returning its index.
///
/// `position()` takes a closure that returns `true` or `false`. It applies
Expand Down
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(intrinsics)]
#![feature(try_find)]
#![feature(is_sorted)]
#![feature(iter_once_with)]
#![feature(lang_items)]
Expand Down
40 changes: 40 additions & 0 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,46 @@ fn test_find_map() {
}
}

#[test]
fn test_try_find() {
let xs: &[isize] = &[];
assert_eq!(xs.iter().try_find(testfn), Ok(None));
let xs: &[isize] = &[1, 2, 3, 4];
assert_eq!(xs.iter().try_find(testfn), Ok(Some(&2)));
let xs: &[isize] = &[1, 3, 4];
assert_eq!(xs.iter().try_find(testfn), Err(()));

let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7];
let mut iter = xs.iter();
assert_eq!(iter.try_find(testfn), Ok(Some(&2)));
assert_eq!(iter.try_find(testfn), Err(()));
assert_eq!(iter.next(), Some(&5));

fn testfn(x: &&isize) -> Result<bool, ()> {
if **x == 2 {
return Ok(true);
}
if **x == 4 {
return Err(());
}
Ok(false)
}
}

#[test]
fn test_try_find_api_usability() -> Result<(), Box<dyn std::error::Error>> {
let a = ["1", "2"];

let is_my_num = |s: &str, search: i32| -> Result<bool, std::num::ParseIntError> {
Ok(s.parse::<i32>()? == search)
};

let val = a.iter().try_find(|&&s| is_my_num(s, 2))?;
assert_eq!(val, Some(&"2"));

Ok(())
}

#[test]
fn test_position() {
let v = &[1, 3, 9, 27, 103, 14, 11];
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(hashmap_internals)]
#![feature(try_find)]
#![feature(is_sorted)]
#![feature(iter_once_with)]
#![feature(pattern)]
Expand Down

0 comments on commit 0a58f58

Please sign in to comment.