21 Rustlings Iterators Solution
Iterators
Rust's journey towards becoming one of the most loved programming languages isn't just by accident. At the heart of its rapidly growing ecosystem are several features that make it both powerful and unique. Among these, iterators stand out as a foundational concept, bridging the gap between functionality and performance. In this section, we'll look into the world of iterators in Rust, unraveling their elegance and efficiency. By understanding and mastering iterators, you'll unlock a significant aspect of Rust's capabilities, enabling you to write code that's both expressive and performant.
Further information
Iterators1.rs
// iterators1.rs
//
// Make me compile by filling in the `???`s
//
// When performing operations on elements within a collection, iterators are essential.
// This module helps you get familiar with the structure of using an iterator and
// how to go through elements within an iterable collection.
//
// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a hint.
// I AM NOT DONE
fn main() {
let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"];
let mut my_iterable_fav_fruits = ???; // TODO: Step 1
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2
assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado"));
assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4
}
In this exercise we're looking at how to make this code compile in this line: let mut my_iterable_fav_fruits = ???;
as our first step.
Before we proceed, let's look at the errors.
Iterators1.rs Errors
⚠️ Compiling of exercises/iterators/iterators1.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
--> exercises/iterators/iterators1.rs:16:38
|
16 | let mut my_iterable_fav_fruits = ???; // TODO: Step 1
| ^ expected expression
error: aborting due to previous error
We get an obvious error due to the missing expression in the variable assignment with the ???
so we have to implement an iterator. Let's solve this.
Iterators1.rs Solution
With iterators we can easily go through an array (or vector in this case) of elements. If you're not familiar with iterators, they're rather easy to use. In our situation here we have a vector which is my_fav_fruits
that has a list of fruits. We see in the next line of code a new mutable variable my_iterable_fav_fruits
where we know we have to complete this. So how do we do it? Easy, we add the .iter()
method to the end of our variable that represents our vector, like this my_fav_fruits.iter()
the full line will now look like this:
let mut my_iterable_fav_fruits = my_fav_fruits.iter(); // TODO: Step 1
The next part of TODO's are in the assert_eq!
section of the code:
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2
assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado"));
assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4
And it looks pretty straightforward to follow the pattern of adding Some(&"fruit")
as we already have some examples with banana
, avocado
, and raspberry
. The only one that should be different is the final one since after raspberry
there is nothing in the vector. So the logical answer would be None
right? Let's try this out.
fn main() {
let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"];
let mut my_iterable_fav_fruits = my_fav_fruits.iter(); // TODO: Step 1
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"custard apple")); // TODO: Step 2
assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"peach")); // TODO: Step 3
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), None); // TODO: Step 4
}
With those changes as defined above we are compiling, great let's move on to the next exercise.
✅ Successfully ran exercises/iterators/iterators1.rs!
🎉 🎉 The code is compiling! 🎉 🎉
Iterators2.rs
// iterators2.rs
// In this exercise, you'll learn some of the unique advantages that iterators
// can offer. Follow the steps to complete the exercise.
// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a hint.
// I AM NOT DONE
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => ???,
}
}
// Step 2.
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
vec![]
}
// Step 3.
// Apply the `capitalize_first` function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
String::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(capitalize_first("hello"), "Hello");
}
#[test]
fn test_empty() {
assert_eq!(capitalize_first(""), "");
}
#[test]
fn test_iterate_string_vec() {
let words = vec!["hello", "world"];
assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}
#[test]
fn test_iterate_into_string() {
let words = vec!["hello", " ", "world"];
assert_eq!(capitalize_words_string(&words), "Hello World");
}
}
Iterators2.rs Errors
⚠️ Compiling of exercises/iterators/iterators2.rs failed! Please try again. Here's the output:
error: expected expression, found `?`
--> exercises/iterators/iterators2.rs:15:24
|
15 | Some(first) => ???,
| -- ^ expected expression
| |
| while parsing the `match` arm starting here
error: aborting due to previous error
our errors are to be expected with our incomplete functions, so let's move on to
Iterators2.rs Solution
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
}
}
// Step 2.
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
words.iter().map(|x| capitalize_first(x)).collect()
}
// Step 3.
// Apply the `capitalize_first` function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
capitalize_words_vector(words).join("")
}
This is one of the more involved solutions we've seen in a while, so let's go over each step and what we did to finish each function and make our code compile and pass the tests.
-
Step 1 -
capitalize_first
function:- We have a function called
capitalize_first
that takes a reference to a string (&str
) as input. - Inside the function, we used the
.chars()
method to get an iterator over the characters in the input string. - We then used a
match
expression to handle two cases:- If the iterator is empty (i.e., the input string is empty), we returned an empty
String
. - If there is at least one character, we used
.next()
to get the first character and then applied the.to_uppercase()
method to convert it to uppercase. - Finally, we used
.collect::<String>()
to collect the uppercase character and the rest of the characters (usingc.as_str()
) into a newString
.
- If the iterator is empty (i.e., the input string is empty), we returned an empty
- We have a function called
-
Step 2 -
capitalize_words_vector
function:- We complete a function called
capitalize_words_vector
that takes a slice of string slices (&[&str]
) as input. - Inside the function, we used
.iter()
to iterate over the elements in the input slice (which are string slices). - For each string slice, we applied the
capitalize_first
function to capitalize its first character and collected the results into aVec<String>
using.map()
and.collect()
.
- We complete a function called
-
Step 3 -
capitalize_words_string
function:- We created a function called
capitalize_words_string
that takes a slice of string slices (&[&str]
) as input. - Inside the function, we reused the
capitalize_words_vector
function to get a vector of capitalized words. - To create a single string, we used the
.join("")
method, which concatenates all the elements in the vector without any spaces between them.
- We created a function called
Overall, the solution focuses on creating reusable functions (capitalize_first
and capitalize_words_vector
) to handle the capitalization of individual words and then uses these functions to achieve the desired output for the exercise (capitalize_words_string
). This approach makes the code more modular and easier to understand.
✅ Successfully tested exercises/iterators/iterators2.rs!
🎉 🎉 The code is compiling, and the tests pass! 🎉 🎉
With that we have passed the test, let's continue to our next exercise.
Iterators3.rs
// iterators3.rs
// This is a bigger exercise than most of the others! You can do it!
// Here is your mission, should you choose to accept it:
// 1. Complete the divide function to get the first four tests to pass.
// 2. Get the remaining tests to pass by completing the result_with_list and
// list_of_results functions.
// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a hint.
// I AM NOT DONE
#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
NotDivisible(NotDivisibleError),
DivideByZero,
}
#[derive(Debug, PartialEq, Eq)]
pub struct NotDivisibleError {
dividend: i32,
divisor: i32,
}
// Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
todo!();
}
// Complete the function and return a value of the correct type so the test passes.
// Desired output: Ok([1, 11, 1426, 3])
fn result_with_list() -> () {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
}
// Complete the function and return a value of the correct type so the test passes.
// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)]
fn list_of_results() -> () {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(divide(81, 9), Ok(9));
}
#[test]
fn test_not_divisible() {
assert_eq!(
divide(81, 6),
Err(DivisionError::NotDivisible(NotDivisibleError {
dividend: 81,
divisor: 6
}))
);
}
#[test]
fn test_divide_by_0() {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
}
#[test]
fn test_divide_0_by_something() {
assert_eq!(divide(0, 81), Ok(0));
}
#[test]
fn test_result_with_list() {
assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])");
}
#[test]
fn test_list_of_results() {
assert_eq!(
format!("{:?}", list_of_results()),
"[Ok(1), Ok(11), Ok(1426), Ok(3)]"
);
}
}
Okay, this is a longer one, so let's breakdown the instructions.
- Complete the
divide
function - Complete the
results_with_list
function - Complete the
list_of_results
function
Iterators3.rs Errors
⚠️ Testing of exercises/iterators/iterators3.rs failed! Please try again. Here's the output:
running 6 tests
test tests::test_divide_0_by_something ... FAILED
test tests::test_not_divisible ... FAILED
test tests::test_divide_by_0 ... FAILED
test tests::test_result_with_list ... FAILED
test tests::test_list_of_results ... FAILED
test tests::test_success ... FAILED
successes:
successes:
failures:
---- tests::test_divide_0_by_something stdout ----
thread 'tests::test_divide_0_by_something' panicked at exercises/iterators/iterators3.rs:26:5:
not yet implemented
---- tests::test_not_divisible stdout ----
thread 'tests::test_not_divisible' panicked at exercises/iterators/iterators3.rs:26:5:
not yet implemented
---- tests::test_divide_by_0 stdout ----
thread 'tests::test_divide_by_0' panicked at exercises/iterators/iterators3.rs:26:5:
not yet implemented
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- tests::test_result_with_list stdout ----
thread 'tests::test_result_with_list' panicked at exercises/iterators/iterators3.rs:75:9:
assertion `left == right` failed
left: "()"
right: "Ok([1, 11, 1426, 3])"
---- tests::test_list_of_results stdout ----
thread 'tests::test_list_of_results' panicked at exercises/iterators/iterators3.rs:80:9:
assertion `left == right` failed
left: "()"
right: "[Ok(1), Ok(11), Ok(1426), Ok(3)]"
---- tests::test_success stdout ----
thread 'tests::test_success' panicked at exercises/iterators/iterators3.rs:26:5:
not yet implemented
failures:
tests::test_divide_0_by_something
tests::test_divide_by_0
tests::test_list_of_results
tests::test_not_divisible
tests::test_result_with_list
tests::test_success
test result: FAILED. 0 passed; 6 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
We see that we have a total 6 tests and they're all failing. The first 3 are because the functions are not yet implemented as our compiler confirms, the next 2 show us that the left
is not matching the right
and our final test is also because it's not yet implemented.
Iterators3.rs Solution
1. Complete the divide
function
Our instructions to complete the divide
function are the following:
// Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
todo!();
}
With this function we actually have a couple of different ways of solving it, we can use the classic if
statement or use a more "Rusty" approach by using match
. Let's first look at the more traditional method of completing this function. Our instructions tell is that we have to calculate if a
can be evenly divided by b
if, it can we do it, if not we return an error. If we look our enum
DivisionError
we can see that we have 2 possible errors, so that gives us a total of 3 possible scenarios:
Ok()
: ifa
is divisible byb
and we dividedErr()
: if we are trying to divide by so ifb == 0
so we return theDividebyZero
error.Err()
: ifa
is not evenly divisible byb
we returnNotDivisible(NotDivisibleError)
Alright let's implement this. Note, that we should implement the if b == 0
first to make sure that b
is not zero before proceeding to a % b
.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
if b == 0 {
// If the divisor is zero, return the DivideByZero error.
Err(DivisionError::DivideByZero)
} else if a % b == 0 {
// If 'a' is evenly divisible by 'b', return the result.
Ok(a / b)
} else {
// If 'a' is not evenly divisible by 'b', return the NotDivisible error.
Err(DivisionError::NotDivisible(NotDivisibleError {
dividend: a,
divisor: b,
}))
}
}
Now let's look at this alternative version using using match:
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
match (a, b) {
// first check if the divisor is zero
(_, 0) => Err(DivisionError::DivideByZero),
// then check if (x, y) are evenly divisible
(x, y) if x % y == 0 => Ok(x / y),
// lastly return an error if it's not even divisible
(x, y) => Err(DivisionError::NotDivisible(NotDivisibleError {
dividend: x,
divisor: y,
})),
}
}
In this version:
- We use a
match
expression to pattern match with a the tuple(a, b)
. - The first arm
(_, 0) => Err(DivisionError::DivideByZero)
matches when the divisorb
is zero, and it returns theDivideByZero
error. - The second arm
(x, y) if x % y == 0 => Ok(x / y)
matches whena
is evenly divisible byb
, and it returns the result of the division asOk
. - The third arm
(x, y)
is a catch-all that matches any other case wherea
is not evenly divisible byb
. It returns theNotDivisible
error with the appropriateNotDivisibleError
struct.
Let's save at this point and see what happens:
test result: FAILED. 4 passed; 2 failed;
Great, it looks like we have passed 4 tests, by completing that first function.
2. Complete the results_with_list
function
These are the instructions to help us complete this function.
// Complete the function and return a value of the correct type so the test passes.
// Desired output: Ok([1, 11, 1426, 3])
fn result_with_list() -> () {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
}
So, let's add a return type, if we look at the rest of the function you can see that we are using a Vec
so, that makes it pretty straight forward to understand what we have to return right?
fn result_with_list() -> Result<Vec<i32>, DivisionError> {
Our function signature should be updated to handle returning a Vec<i32>
if all is Ok
or a DivisionError
if it's not so we put that in the function signature to complete it. Next let's format the division_results
function a little differently from the one liner to the having each method start on a new line.
let division_results = numbers
.into_iter()
.map(|n| divide(n, 27))
.collect();
This also give us the opportunity to add the .collect()
method which was missing and essentially when "collecting" elements back into a vector. This also makes it easier to read in my opinion.
Finally to complete this function we have to return a value. Right now if we compile we'd see this message:
40 | fn result_with_list() -> Result<Vec<i32>, DivisionError> {
| ---------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<Vec<i32>, DivisionError>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
|
= note: expected enum `Result<Vec<i32>, DivisionError>`
found unit type `()`
help: consider returning the local binding `division_results`
|
45 ~ .collect();
46 + division_results
Luckily the Rust's error message is explicit in telling you what you should return. If we add division_results
on line 46
we get this printout:
running 6 tests
test tests::test_divide_0_by_something ... ok
test tests::test_divide_by_0 ... ok
test tests::test_not_divisible ... ok
test tests::test_result_with_list ... ok
test tests::test_success ... ok
test tests::test_list_of_results ... FAILED
Alright we're almost there.
3. Complete the list_of_results
function
This is our final function to complete as you can see we are passing all our tests except our final one, so let's dig into the instructions for this one.
// Complete the function and return a value of the correct type so the test passes.
// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)]
fn list_of_results() -> () {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
}
Our starting point looks very similar to our previous function but the desired output is different. As it seems to be wanting one Ok
result for every number vs a Vec<i32>
. So how do we solve this?
Instead of wrapping our Vec<i32>
in Result
we wrap our Result
in a Vec<Result<i32>>
like so:
fn list_of_results() -> Vec<Result<i32, DivisionError>> {
and then like in our previous example, we use the .collect()
method and returndivision_results
. Here's the full block for reference.
fn list_of_results() -> Vec<Result<i32, DivisionError>> {
let numbers = vec![27, 297, 38502, 81];
let division_results: Vec<Result<i32, DivisionError>> = numbers
.into_iter()
.map(|n| divide(n, 27))
.collect();
division_results
}
- We specify the return type as
Vec<Result<i32, DivisionError>>
to match the desired output. - We use the
collect
method on the iterator of division results. This method collects the individual results into aVec<Result<i32, DivisionError>>
. - We store the collected results in the
division_results
variable. - Finally, we return
division_results
, which is aVec
containing individualOk
results.
This should make the test pass with the desired output [Ok(1), Ok(11), Ok(1426), Ok(3)]
.
✅ Successfully tested exercises/iterators/iterators3.rs!
🎉 🎉 The code is compiling, and the tests pass! 🎉 🎉
Iterators4.rs
// iterators4.rs
// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a hint.
// I AM NOT DONE
pub fn factorial(num: u64) -> u64 {
// Complete this function to return the factorial of num
// Do not use:
// - return
// Try not to use:
// - imperative style loops (for, while)
// - additional variables
// For an extra challenge, don't use:
// - recursion
// Execute `rustlings hint iterators4` for hints.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn factorial_of_0() {
assert_eq!(1, factorial(0));
}
#[test]
fn factorial_of_1() {
assert_eq!(1, factorial(1));
}
#[test]
fn factorial_of_2() {
assert_eq!(2, factorial(2));
}
#[test]
fn factorial_of_4() {
assert_eq!(24, factorial(4));
}
}
Okay it seems we have a little challenge here to create a function that returns the factorial of num
we also have some added limitations.
- Do not use
return
- Try not use
for
orwhile
- Additional variables
- Extra credit: Don't use recursion
Iterators4.rs Errors
⚠️ Compiling of exercises/iterators/iterators4.rs failed! Please try again. Here's the output:
error[E0308]: mismatched types
--> exercises/iterators/iterators4.rs:6:31
|
6 | pub fn factorial(num: u64) -> u64 {
| --------- ^^^ expected `u64`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
|
help: consider returning the local binding `num`
|
6 ~ pub fn factorial(num: u64) -> u64 {
7 + num
|
error: aborting due to previous error
Our errors don't tell us much in this case we know we have an incomplete function
Iterators4.rs Solution
So our challenge is pretty wide open, meaning we do have a possible few different ways to solve this, but let's try to stick as closely as possible to the requirements. It's clear this exercise is made for us to iterators and their associated methods.
But before we do that let's make sure we understand the goal, just incase some of us need to brush up on our math terminology, let's make sure we understand the definition of a factorial.
What is a Factorial?
Factorials are a fascinating concept in mathematics that involves multiplying a sequence of numbers together.
Simply put, a factorial of a number is the product of all positive integers from 1 to that number. For example, the factorial of 5 is calculated as:
5! = 5 × 4 × 3 × 2 × 1 = 120
In Rust, you can do this easily and efficiently using a few simple methods.
So we said that to calculate a factorial we need a sequence of numbers from 1 to that numbers so how do we represent this in Rust?1..num
seems like a logical choice. Right, so now we have to take this range and iterate over it but instead of using iter
let's use into_iter
which allows us to take ownership of each element and then we use .product()
which we haven't used before but comes in handy in situations like these, more on product below.
pub fn factorial(num: u64) -> u64 {
(1..=num).into_iter().product()
}
.product()
Notes
product()
method is a convenient way to calculate the product of all the numbers in an iterator or a range. In the context of calculating a factorial, think of it like this:
Let's say you want to find the factorial of a number, like 5!
(read as "5 factorial"). This means you want to multiply all the positive whole numbers from 1
to 5
together: 1 * 2 * 3 * 4 * 5
.
Using an iterator or a range, you can create a sequence of these numbers: 1, 2, 3, 4, 5
. Then, when you apply the product()
method to this sequence, it will perform the multiplication for you and give you the result, which in this case is 120
.
So, in essence, product()
takes a sequence of numbers and multiplies them all together, making it handy for tasks like calculating factorials. It's like a built-in calculator for products in Rust!
So if we save this we will successfully compile.
Let's move on to our final exercise.
Iterators5.rs
// iterators5.rs
// Let's define a simple model to track Rustlings exercise progress. Progress
// will be modelled using a hash map. The name of the exercise is the key and
// the progress is the value. Two counting functions were created to count the
// number of exercises with a given progress. These counting functions use
// imperative style for loops. Recreate this counting functionality using
// iterators. Only the two iterator methods (count_iterator and
// count_collection_iterator) need to be modified.
// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a hint.
//
// Make the code compile and the tests pass.
// I AM NOT DONE
use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Progress {
None,
Some,
Complete,
}
fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
let mut count = 0;
for val in map.values() {
if val == &value {
count += 1;
}
}
count
}
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
// map is a hashmap with String keys and Progress values.
// map = { "variables1": Complete, "from_str": None, ... }
todo!();
}
fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
let mut count = 0;
for map in collection {
for val in map.values() {
if val == &value {
count += 1;
}
}
}
count
}
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
// collection is a slice of hashmaps.
// collection = [{ "variables1": Complete, "from_str": None, ... },
// { "variables2": Complete, ... }, ... ]
todo!();
}
#[cfg(test)]
mod tests {
use super::*;
// tests removed for space...
Our instructions are to finish 2 functions using iterators instead of imperative style loops.
Iterators5.rs Errors
⚠️ Testing of exercises/iterators/iterators5.rs failed! Please try again. Here's the output:
running 4 tests
test tests::count_complete ... FAILED
test tests::count_equals_for ... FAILED
test tests::count_collection_complete ... FAILED
test tests::count_collection_equals_for ... FAILED
For reference, here are failing tests, but this is expected as they are not implemented yet.
Iterators5.rs Solution
count_iterator
solution
For the first function count_iterator
we have additional note to help us complete our function
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
// map is a hashmap with String keys and Progress values.
// map = { "variables1": Complete, "from_str": None, ... }
todo!();
}
Here's a way we can solve this function
- We use
map.values()
to obtain an iterator over the values in the hashmap. - We use
filter
to keep only the values that are equal to the givenvalue
. - Finally, we call
count()
to count the number of elements that match the condition.
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
map.values()
.filter(|&&val| val == value)
.count()
}
This should complete our function and move us closer to compiling.
count_collection_iterator
solution
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
// collection is a slice of hashmaps.
// collection = [{ "variables1": Complete, "from_str": None, ... },
// { "variables2": Complete, ... }, ... ]
todo!();
}```
again a similar situation where we have to complete our function with additional notes and instructions in the comments.
We can approach this function like so:
- We use `collection.iter()` to get an iterator over the slices of hashmaps.
- We use `flat_map` to flatten each slice of hashmap values into a single iterator of values.
- Then, we apply the same `filter` and `count` operations as in `count_iterator` to count the matching values across all hashmaps in the collection.
Our final code block looks like this:
```rust
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
collection.iter()
.flat_map(|map| map.values())
.filter(|&&val| val == value)
.count()
}
And with this we are compiling and passing our tests!
I apologize for the oversight. Let's wrap up your blog with a suitable conclusion.
Conclusion
In these rustlings
exercises, we've traversed the intriguing waters of Rust's iterators, pattern matching, and handling results. Rust, being a language that prioritizes safety and efficiency, provides tools like match
and Result
to handle various scenarios gracefully. The real power of Rust lies in its expressive type system and the way it allows us to catch errors at compile time. By practicing with iterators and learning to handle errors the "Rusty" way, we're not just writing more concise code; we're embracing a paradigm that avoids common pitfalls seen in other languages.
Through our journey of solving the provided exercises, we have reinforced the idea that Rust provides an elegant balance between performance and safety. Its functional capabilities, combined with its low-level control, make it a top choice for systems programming and beyond. If you're still new to Rust, I hope this exploration has provided clarity and fueled your interest to dig deeper. If you're already a seasoned Rustacean, I hope this was a refreshing recap and perhaps even offered a new perspective or two.
In the world of programming, continuous learning is the key. As we've seen today, even seemingly simple exercises can offer valuable insights. Keep coding, keep exploring, and always strive to better your understanding. The Rust community is vast, welcoming, and ever-growing. With tools like rustlings
and a plethora of online resources, your Rust journey is bound to be exciting and enlightening. Happy coding!