diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 733d414d44465..5e00486fc68e9 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1327,7 +1327,7 @@ pub trait Iterator { /// `take(n)` yields elements until `n` elements are yielded or the end of /// the iterator is reached (whichever happens first). /// The returned iterator is a prefix of length `n` if the original iterator - /// contains at least `n` elements, otherwise it contains all of the + /// contains at least `n` elements, otherwise it containss all of the /// (fewer than `n`) elements of the original iterator. /// /// # Examples @@ -4060,6 +4060,40 @@ pub trait Iterator { { unreachable!("Always specialized"); } + + /// 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`. + /// + /// Performance: + /// This method checks the whole iterator, which takes O(n) time. + /// If the iterator is sorted, or a hash map please use the appropriate method instead. + /// + /// Example: + /// ``` + /// #![feature(iter_contains)] + /// assert!([1, 2, 3].iter().map(|&x| x * 3).contains(&6)); + /// assert!(![1, 2, 3].iter().contains(&4)); + /// // You can check if a String is in a string slice iterator. + /// assert!(["a", "b", "c"].iter().contains(&"b".to_owned())); + /// // You can also check if a String iterator contains a string slice. + /// assert!(["a".to_owned(), "b".to_owned(), "c".to_owned()].iter().contains(&"b")); + /// ``` + /// + #[unstable(feature = "iter_contains", reason = "new API", issue = "127494")] + fn contains(&mut self, item: Q) -> bool + where + Q: PartialEq, + Self: Sized, + { + for element in self { + if item == element { + return true; + } + } + return false; + } } /// Compares two iterators element-wise using the given function. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0f82f01e57a71..1bfffa0f58dd4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -171,6 +171,7 @@ #![feature(ip)] #![feature(is_ascii_octdigit)] #![feature(isqrt)] +#![feature(iter_contains)] #![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(offset_of_nested)] diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index 93ef9c0812b16..d2244b9aa3307 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -616,6 +616,65 @@ fn test_next_chunk() { let mut it = std::iter::repeat_with(|| panic!()); assert_eq!(it.next_chunk::<0>().unwrap(), []); } +#[test] +fn test_happy_path_item_not_in_iterator() { + assert!(![1i32, 2i32, 3i32].iter().contains(&4i32)); +} + +#[test] +fn test_edge_case_handling_none_values() { + assert!([Some(2i32), Option::::None].iter().contains(&None)); + assert!([Some(2i32), Option::::None].iter().contains(&Some(2i32))); +} + +#[test] +fn test_edge_case_handling_empty_iterator() { + assert!(!Vec::::new().iter().contains(&1i32)); +} + +#[test] +fn test_edge_case_handling_iterator_with_duplicates() { + assert!([1i32, 2i32, 2i32, 3i32].iter().contains(&2i32)); +} + +#[test] +/// Tests that short-circuiting works correctly when using `contains` +/// When you run the function, it should move the iterator forward after the first appearance of the item +fn test_short_circuiting() { + let vector: Vec = vec![1i32, 2i32, 3i32, 1i32, 1i32]; + let mut iterator = vector.into_iter(); + assert!(iterator.contains(1i32)); + assert_eq!(iterator.next(), Some(2)); + assert!(!iterator.contains(4i32)); + assert_eq!(iterator.next(), None); +} + +#[test] +fn test_edge_case_handling_iterator_with_custom_struct() { + #[derive(PartialEq)] + struct Item { + value: i32, + } + assert!([Item { value: 1i32 }, Item { value: 2i32 }].iter().contains(&Item { value: 2i32 })); +} + +#[test] +fn test_str_iterator_contains_string() { + assert!(["a", "b", "c"].iter().contains(&"b".to_owned())); + assert!(!["a", "b", "c"].iter().contains(&"d".to_owned())); +} + +#[test] +fn test_str_iterator_contains_string_slice() { + assert!(["a", "b", "c"].iter().contains(&"b")); + assert!(!["a", "b", "c"].iter().contains(&"d")); +} + +#[test] +fn test_string_iterator_contains_str_slice() { + assert!(["a".to_owned(), "b".to_owned(), "c".to_owned()].iter().contains(&"b")); + assert!(!["a".to_owned(), "b".to_owned(), "c".to_owned()].iter().contains(&"d")); +} // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 83a615fcd8be3..08d633b3c2c6f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -89,6 +89,7 @@ #![feature(const_mut_refs)] #![feature(const_pin)] #![feature(const_waker)] +#![feature(iter_contains)] #![feature(never_type)] #![feature(unwrap_infallible)] #![feature(pointer_is_aligned_to)] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 7755a9b9748be..c667dc1e436f8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -6074,6 +6074,17 @@ The tracking issue for this feature is: None. ------------------------ +"##, + }, + Lint { + label: "contains", + description: r##"# `contains` + +The tracking issue for this feature is: [#94047] + +[#94047]: https://github.com/rust-lang/rust/issues/94047 + + The `rustc` compiler has certain pluggable operations, that is, functionality that isn't hard-coded into the language, but is implemented in libraries, with a special marker to tell the compiler diff --git a/tests/ui/suggestions/deref-path-method.rs b/tests/ui/suggestions/deref-path-method.rs index 0281cdb6b37cf..03cca7f8e582f 100644 --- a/tests/ui/suggestions/deref-path-method.rs +++ b/tests/ui/suggestions/deref-path-method.rs @@ -1,6 +1,6 @@ fn main() { let vec = Vec::new(); Vec::contains(&vec, &0); - //~^ ERROR no function or associated item named `contains` found for struct `Vec<_, _>` in the current scope + //~^ ERROR `Vec<_, _>` is not an iterator [E0599] //~| HELP the function `contains` is implemented on `[_]` } diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index b27d9aef06614..568e6f97e9752 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -1,8 +1,8 @@ -error[E0599]: no function or associated item named `contains` found for struct `Vec<_, _>` in the current scope +error[E0599]: `Vec<_, _>` is not an iterator --> $DIR/deref-path-method.rs:3:10 | LL | Vec::contains(&vec, &0); - | ^^^^^^^^ function or associated item not found in `Vec<_, _>` + | ^^^^^^^^ `Vec<_, _>` is not an iterator | note: if you're trying to build a new `Vec<_, _>` consider using one of the following associated functions: Vec::::new @@ -11,6 +11,9 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll Vec::::from_raw_parts and 4 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + = note: the following trait bounds were not satisfied: + `Vec<_, _>: Iterator` + which is required by `&mut Vec<_, _>: Iterator` help: the function `contains` is implemented on `[_]` | LL | <[_]>::contains(&vec, &0);