Lesson 4
Iterators and Enumerators in Rust
Introduction to Iterators and Enumerators in Rust

Hello! In this lesson, we are going to explore a fundamental aspect of Rust programming: Iterators and Enumerators. Iterators are an essential tool in Rust, allowing you to traverse sequences of data efficiently. They offer a convenient and idiomatic way to manipulate collections.

By the end of this lesson, you'll be proficient in iterating over arrays, vectors, strings, and hash maps. We'll break down each topic into understandable and practical steps, so you can follow along and implement them in your own code seamlessly.

Let's dive in!

Introducing `iter`

Before we delve into specific examples of iterating over various types of collections, let's first introduce two fundamental methods in Rust: iter and enumerate.

The iter method is commonly used to create an iterator from a collection. This method is available for arrays, vectors, and hash maps, allowing you to traverse through them element by element.

Rust
1fn main() { 2 let numbers = [1, 2, 3, 4, 5]; 3 4 // Using `iter` to get an iterator over the array 5 for value in numbers.iter() { 6 println!("Value: {}", value); 7 } 8} 9/* Output: 10 Value: 1 11 Value: 2 12 Value: 3 13 Value: 4 14 Value: 5 15*/

The code for value in numbers.iter() uses the iter method to create an iterator over the array numbers. The for loop then iterates over each element. Within the loop, each element (referred to as value) is printed. For each iteration, the placeholder {} is replaced with the current value.

Pairing `iter` and `enumerate`

The enumerate method builds upon the base iterator to provide a sequence of pairs, where each pair consists of an index and a reference to the value at that index. This is particularly useful when you need to keep track of the position of each item within the collection. Let's take a look.

Rust
1fn main() { 2 // Defining an array 3 let numbers = [1, 2, 3, 4, 5]; 4 5 // Using `iter().enumerate()` on the array 6 for (index, value) in numbers.iter().enumerate() { 7 println!("Index: {}, Value: {}", index, value); 8 } 9} 10/* Output 11 Index: 0, Value: 1 12 Index: 1, Value: 2 13 Index: 2, Value: 3 14 Index: 3, Value: 4 15 Index: 4, Value: 5 16*/

for (index, value) in numbers.iter().enumerate() uses both the iter and enumerate methods. iter creates an iterator over the array, and enumerate transforms this iterator into one that yields pairs of (index, value), where index is the position of the element in the array and value is the reference to the element.

Iterating Over a Vector

Vectors are similar to arrays, but their size can change dynamically. They are more flexible for most situations that involve collections. The syntax for iter and enumerate are the same for both vectors and arrays.

Rust
1fn main() { 2 // Defining a vector 3 let vec = vec![1, 2, 3, 4, 5]; 4 5 // Iterating over the vector using .iter() 6 for value in vec.iter() { 7 println!("{}", value); 8 } 9 10 // Iterating with enumeration to access indices 11 for (index, value) in vec.iter().enumerate() { 12 println!("Index: {}, Value: {}", index, value); // Prints index and value pairs 13 } 14}
Iterating Over a String

For string data types, we use .chars() to iterate over each character in a string.

Rust
1fn main() { 2 // Defining a string slice 3 let s = "hello"; 4 5 // Iterating over the string using .chars() 6 for ch in s.chars() { 7 println!("{}", ch); // Prints each character on a new line 8 } 9}

To access both the index and character of each element, use .chars().enumerate()

Rust
1fn main() { 2 let s = "rust"; 3 // Iterating with enumeration to access indices 4 for (index, ch) in s.chars().enumerate() { 5 println!("Index: {}, Char: {}", index, ch); 6 } 7} 8/* Output 9 Index: 0, Char: r 10 Index: 1, Char: u 11 Index: 2, Char: s 12 Index: 3, Char: t 13*/
Iterating Over a HashMap

HashMaps store key-value pairs and provide efficient lookup. There are two ways to iterate over the key/value pairs of a HashMap. Rust's standard library offers the .iter() method to iterate over these pairs. In addition, you can pass a reference to the HashMap, and Rust automatically calls the iter method on the reference. HashMaps are unordered collections, so the order in which key/value pairs are iterated over is not guaranteed.

Rust
1use std::collections::HashMap; 2 3fn main() { 4 5 // Defining a HashMap 6 let mut map = HashMap::new(); 7 map.insert("a", 1); 8 map.insert("b", 2); 9 map.insert("c", 3); 10 11 // Iterating over the HashMap using iter 12 println!("Iterating over a HashMap:"); 13 for (key, value) in map.iter() { 14 println!("Key: {}, Value: {}", key, value); 15 } 16 17 // Iterating over the HashMap using reference 18 for (key, value) in &map { 19 println!("Key: {}, Value: {}", key, value); 20 } 21} 22/* Possible Outputs for Each Loop 23 Key: a, Value: 1 24 Key: b, Value: 2 25 Key: c, Value: 3 26*/

The outputs might differ in the order elements are printed since HashMaps are unordered collections.

Summary and Next Steps

Great work today! You’ve learned how to efficiently iterate over arrays, vectors, strings, and hash maps in Rust. Each of these examples showcased different ways to create and use iterators, reinforcing their versatility and power.

Now that you've gained a solid understanding of iterators and enumerators, it's time to put your skills to the test. Dive into the practice exercises ahead to solidify your learning and explore the full potential of iterators in Rust. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.