From b2b759726990bd4c863eb9cd2c8b397e6aefbda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?= Date: Wed, 30 Oct 2024 09:15:56 +0100 Subject: [PATCH] 06: iterators --- Cargo.toml | 3 + .../lessons/06_closures_iterators/index.md | 15 ++++- .../iterator_exhaustion.rs | 58 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 content/lessons/06_closures_iterators/iterator_exhaustion.rs diff --git a/Cargo.toml b/Cargo.toml index 465cca1..a27973c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,9 @@ path = "content/lessons/06_closures_iterators/closures_capturing.rs" [[bin]] name = "06_closures_fun" path = "content/lessons/06_closures_iterators/closures_fun.rs" +[[bin]] +name = "06_iterator_exhaustion" +path = "content/lessons/06_closures_iterators/iterator_exhaustion.rs" [[bin]] name = "07_box" diff --git a/content/lessons/06_closures_iterators/index.md b/content/lessons/06_closures_iterators/index.md index f7c96ef..979edc8 100644 --- a/content/lessons/06_closures_iterators/index.md +++ b/content/lessons/06_closures_iterators/index.md @@ -57,8 +57,21 @@ More examples will be seen when working with iterators. In Rust, there is no hierarchy of types for collections (because there is no inheritance in general). Instead, what makes a collection is that it can be iterated over. +A usual way in Rust to perform an iteration over something, be it a range of values or items in a collection, is creating a (lazy) iterator over it and transforming it using *iterator adaptors*. For example, if `T: Iterator`, then `T::map()` creates a `Map` adaptor. Once a final iterator is created, it has to be actually activated, which is most commonly done by: +- exhausting it with the `for` loop, +- manually iterating over it using `next()` calls, +- collecting its contents into inferred collection (`collect()`), +- consuming it with a *consuming adaptor* (e.g., `sum()`, `count`), + +{{ include_code_sample(path="lessons/06_closures_iterators/iterator_exhaustion.rs", language="rust") }} + + +Iterators are highly optimised, so they are high-level code that compiles down to simple and optimised machine code (intended as _zero-cost abstractions_). + We'll go through the official [docs](https://doc.rust-lang.org/stable/std/iter/). -Most methods are defined in the [Iterator trait](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html). +- Most methods are defined in the [Iterator trait](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html). +- [IntoIterator](https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html) is also worth noting, because it makes types work with the `for` loop. +- For completeness, there is [FromIterator](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html), which is required for `collect()` to work. # Reading diff --git a/content/lessons/06_closures_iterators/iterator_exhaustion.rs b/content/lessons/06_closures_iterators/iterator_exhaustion.rs new file mode 100644 index 0000000..641183f --- /dev/null +++ b/content/lessons/06_closures_iterators/iterator_exhaustion.rs @@ -0,0 +1,58 @@ +use std::collections::HashSet; + +fn main() { + // Various ways to create a String. + let mut strings = [ + String::new(), + String::from("a"), + "b".into(), + "c".to_owned(), + "d".to_string(), + "e".chars().collect(), + ]; + + // `iter()` is a usual method that creates an iterator over immutable references to the collection's items. + let _all_len_0_or_1 = strings + .iter() + .filter(|s| !s.is_empty()) + .all(|s| s.len() == 1); + + // `iter_mut()` is a usual method that creates an iterator over mutable references to the collection's items. + for s in strings.iter_mut().map_while(|s| match s.as_str() { + "c" => None, + _ => Some(s), + }) { + *s = s.replace("b", "aba"); + } + + // This is equivalent code. + // `for` is usually more idiomatic, but `for_each` is sometimes cleaner and sometimes faster. + strings + .iter_mut() + .map_while(|s| match s.as_str() { + "c" => None, + _ => Some(s), + }) + .for_each(|s| *s = s.replace("b", "aba")); + + // `into_iter()` is a method from `IntoIterator` trait that converts a collection to an iterator + let mut empty_strings_iter = strings.into_iter().map(|mut s| { + s.clear(); + s + }); + + // This is a set of empty Strings... + let empty_strings_set = empty_strings_iter.clone().collect::>(); + + // And this is a Vec of immutable references to empty Strings. + let empty_string_refs_vec = empty_strings_set.iter().collect::>(); + + // equivalent to `empty_string_refs_vec.into_iter()` + for s in empty_string_refs_vec { + println!("{}", s) + } + + while let Some(s) = empty_strings_iter.next_back() { + assert!(s.is_empty()); + } +}