30 days of Rust - Day Fourteen - Iterators
I took the weekend off of code to visit family and friends. 😅 I guess one needs time off every now and then, let's get back right where we left off.
Day 14 - Iterators
Iterators allow us to go over a collection of values, one item at a time. This can be a list of numbers, text or even a Map. If you have done a bit of coding before, chances are you have used a for loop. In rust a for loop looks like;
let v1 = vec![1,2,3];
for num in v1 {
//for each value in the v1 vector
println!("{}", num)
}
This output's
1
2
3
The Iterator Trait
All iterators in Rust implement a trait named Iterator in the standard library.
It is defined as such;
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// methods with default implementations elided
}
It stores the type Item
which is the type of elements being iterated over and has one required method next
which basically just returns the next element inside the iterator or a None
in the case that the iteration has reached the end.
Basic Implementation
In order to make a vector iterable, we call the .iter()
method on the vector. Each time we need to get a value inside the iteration, we call the .next()
method. This advances the iteration by one step.
let v1 = vec![1,2,3]
let mut v1_iter = v1.iter();
println!("{:?}", v1_iter.next()); // Some(&1)
println!("{:?}", v1_iter.next());// Some(&2)
println!("{:?}", v1_iter.next());// Some(&3)
println!("{:?}", v1_iter.next());// None
Iterators have some cool methods you should have a look at, such as the .sum()
method, which calls the next
method inside its implementation and returns a total of all the numbers inside the iterable.
let v1 = vec![5,4,3]
let mut v1_iter = v1.iter();
let total:i32 = v1_iter.sum(); //12
Using closures
We can also use closures together with iterators. For a refresher on closures, have a look at my day 13 post here.
Let's say we need to filter a list of employees by their age. We can use the .filter
method on the iterable, which takes a closure which applies an operation on each element and returns a boolean (true/false). A list will be returned including only the items where the operation has a true value.
#[derive(Debug)]
struct Employee {
name:String,
age: u32,
}
fn older_than_age(employees: Vec<Employee>, age: u32) -> Vec<Employee>{
employees.into_iter().filter(|emp| emp.age>= age).collect()
}
Lets create a list of 3 employees;
let employees = vec![
Employee{
name: String::from("Masai"),
age:24
},
Employee{
name: String::from("Papa"),
age: 38
},
Employee{
name: String::from("Dora the Explorer"),
age:17
},
];
Now to get only employees who are above 21 years of age;
println!("{:?}", older_than_age(employees, 35));
Outputs;
[Employee { name: "Masai", age: 24 }, Employee { name: "Papa", age: 38 }]
Conclusion
Iterators are an advanced concept in Rust which allow for better performance over a for loop, yet they are not too hard to implement.
When used together with Closures, we can build really clean and efficient code.
Thanks for spending some time learning Rust with me. Until next time. Peace ✌🏼 .