Skip to content

Commit

Permalink
06: iterators
Browse files Browse the repository at this point in the history
  • Loading branch information
wprzytula committed Oct 30, 2024
1 parent e3d3b3a commit b2b7597
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
15 changes: 14 additions & 1 deletion content/lessons/06_closures_iterators/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` 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

Expand Down
58 changes: 58 additions & 0 deletions content/lessons/06_closures_iterators/iterator_exhaustion.rs
Original file line number Diff line number Diff line change
@@ -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::<HashSet<_>>();

// And this is a Vec of immutable references to empty Strings.
let empty_string_refs_vec = empty_strings_set.iter().collect::<Vec<_>>();

// 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());
}
}

0 comments on commit b2b7597

Please sign in to comment.