What these built-ins do
Finding the smallest value, the largest value and the total of a list are jobs that come up all the time, and each takes a small loop to write by hand. Rust iterators have them built in. Start from a vector with iter(), then call one of:
min(), which finds the smallest value;max(), which finds the largest value;sum(), which adds all the values up.
let prices = vec![4, 9, 2, 7];
println!("{}", prices.iter().min().unwrap());
println!("{}", prices.iter().max().unwrap());
let total: i32 = prices.iter().sum();
println!("{}", total);
2
9
22
min and max return an Option
An empty vector has no smallest value, so min() and max() can't always return a number. They return an Option: Some(value) when there is one, None when the vector is empty. On [4, 9, 2, 7] the result of min() is Some(2). The call unwrap() takes the value out of the Some.
The value inside is a reference into the vector, because iter() borrows the items. Put a * in front to get the plain number:
let smallest = *prices.iter().min().unwrap();
Printing with println! works with the reference as it is, as the first example shows. The star matters when you store the number or do arithmetic with it.
sum needs a type
Numbers come in several types, and sum() can produce any of them, so it needs to be told which one. The usual way is to write the type on the variable: let total: i32 = prices.iter().sum();. Without it, the compiler stops with "type annotations needed". Another way is prices.iter().sum::<i32>().
Watch out for an empty vector
On an empty vector, min() and max() give None, and calling unwrap() on None makes the program panic. When the vector may be empty, give a fallback with unwrap_or(&0) instead; the & is there because the value that would be inside is a reference. The sum of an empty vector is not a problem: it is 0.
For an empty vector of integers, min() and max() both print as None, sum() gives 0, and min().unwrap_or(&0) gives 0.
What "smallest" means for other types
The built-ins compare values with their natural order. For numbers that is numeric order. For strings it is alphabetical order, not length: on ["pear", "fig", "banana"], min() gives Some("banana") and max() gives Some("pear"). To pick by another rule, such as the shortest word, the iterator has min_by_key and max_by_key. Decimals (f64) have no natural total order because of NaN, so min() and max() are not available for them directly.
Compared with a loop
A hand-written loop finds the same largest value as the built-in: on [4, 9, 2, 7] both give 9. The loop version needs a starting value and an if; the built-in says what you want in one chain, and the Option forces you to think about the empty case. Use the loop when you need extra logic inside it, and the built-in when you only need the answer.
The exercise, step by step
minutes holds how many minutes you practiced on each day of a week. The task is to store the shortest day, the longest day and the total, using these built-ins.
- Shortest. Call
min()onminutes.iter(), thenunwrap(), with a*in front, and store it inshortest. - Longest. The same chain with
max(), stored inlongest. - Total. Call
sum()onminutes.iter()and store it intotal, declared with the typei32.
let minutes = vec![25, 40, 15, 60, 30, 45, 20];
let shortest = *minutes.iter().min().unwrap();
let longest = *minutes.iter().max().unwrap();
let total: i32 = minutes.iter().sum();
println!("shortest = {}", shortest);
println!("longest = {}", longest);
println!("total = {}", total);
It prints:
shortest = 15
longest = 60
total = 235
Key takeaways
iter().min()anditer().max()return anOptionholding a reference; useunwrap()and a*to get the number.iter().sum()needs a type, usually written on the variable.- An empty vector makes
unwrap()panic formin()andmax(), and gives 0 forsum().
