Skip to content

Commit

Permalink
Add experimental Iterator::contains
Browse files Browse the repository at this point in the history
  • Loading branch information
leb-kuchen committed Jan 2, 2025
1 parent eeeff9a commit 35193cb
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 0 deletions.
33 changes: 33 additions & 0 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,39 @@ pub trait Iterator {
self.try_fold((), check(f)) == ControlFlow::Break(())
}

/// Returns `true` if the iterator contains a value.
///
/// `contains()` is short-circuiting; in other words, it will stop processing
/// as soon as the function finds the item in the `Iterator`.
///
/// This method checks the whole iterator, which is O(n). If the iterator is a sorted
/// slice, [`binary_search`](slice::binary_search) may be faster. If this is an iterator
/// on collections that have a `.contains()` or `.contains_key()` method (such as
/// `HashMap` or `BtreeSet`, using those methods directly will be faster.
///
/// # Examples
/// Basic usage:
/// ```
/// #![feature(iter_contains)]
/// assert_eq!(true, [1, 2, 3].iter().contains(2));
/// assert_eq!(false, [1, 2, 3].iter().contains(5));
/// ```
/// [`Iterator::contains`] can be used where [`slice::contains`] cannot be used:
/// ```
/// #![feature(iter_contains)]
/// let s = [String::from("a"), String::from("b"), String::from("c")];
/// assert_eq!(s.iter().contains("b"), s.iter().any(|e| e == "b"));
/// ```
#[inline]
#[unstable(feature = "iter_contains", reason = "new API", issue = "127494")]
fn contains<Q: ?Sized>(&mut self, item: Q) -> bool
where
Q: PartialEq<Self::Item>,
Self: Sized,
{
self.any(|elem| item == elem)
}

/// Searches for an element of an iterator that satisfies a predicate.
///
/// `find()` takes a closure that returns `true` or `false`. It applies
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
#![feature(internal_impls_macro)]
#![feature(ip)]
#![feature(is_ascii_octdigit)]
#![feature(iter_contains)]
#![feature(lazy_get)]
#![feature(link_cfg)]
#![feature(non_null_from_ref)]
Expand Down
10 changes: 10 additions & 0 deletions library/core/src/slice/iter/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,16 @@ macro_rules! iterator {
false
}

fn contains<Q: ?Sized>(&mut self, item: Q) -> bool
where
Q: PartialEq<Self::Item>,
Self: Sized,
{
// specialize
self.any(|elem|item == elem)

}

// We override the default implementation, which uses `try_fold`,
// because this simple implementation generates less LLVM IR and is
// faster to compile.
Expand Down
7 changes: 7 additions & 0 deletions library/core/src/str/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,13 @@ impl Iterator for Bytes<'_> {
self.0.any(f)
}

fn contains<Q: ?Sized>(&mut self, item: Q) -> bool
where
Q: PartialEq<Self::Item>,
{
self.0.contains(item)
}

#[inline]
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where
Expand Down
10 changes: 10 additions & 0 deletions library/core/tests/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,16 @@ fn test_any() {
assert!(!v.iter().any(|&x| x > 100));
assert!(!v[..0].iter().any(|_| panic!()));
}
#[test]
fn test_iterator_contains() {
let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]);
assert_eq!(true, v.iter().contains(3));
assert_eq!(v.iter().contains(3), v.iter().any(|&x| x == 3));
assert_eq!(false, v.iter().contains(10));
assert_eq!(v.iter().contains(10), v.iter().any(|&x| x == 10));
assert_eq!(true, Iterator::contains(&mut (1..=5), 3));
assert_eq!(false, Iterator::contains(&mut (1..=5), 10));
}

#[test]
fn test_find() {
Expand Down

0 comments on commit 35193cb

Please sign in to comment.