Reputation: 43
I am working through the rustlings exercises to learn Rust. I have reached the iterator3.rs exercise and am stuck. This exercise asks me to provide a line of code that will map results from one type to another as part of the operation; I need to fill in the line x= with the correct operation. There are two parts - the first reads in part:
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let x = ???
assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");
The next is the same with a slightly different format for the assertion of the output:
assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");
I believe I understand that the first instance needs to return a Result that contains either a Vector of i32 or some error type. The second needs to return a Vector of Results that each have a i32 or an error type.
I am, however, generally having difficulty understanding how to determine what type is returned by the combinations of into_iter, map, and collect. I could use some help in learning how to reason about it or in getting compiler assistance.
Here is where I am so far:
I don't understand what the resultant type of division_results is. I have tried to use compiler error messages as well as the answer of this question to find out, but the results are opaque to me, perhaps because of lazy evaluation. For example, just replacing x with division_results so that assert will show the types, like this:
assert_eq!(format!("{:?}", division_results), "Ok([1, 11, 1426, 3])");
Gives me this result:
left: `"Map { iter: IntoIter([27, 297, 38502, 81]) }"`,
right: `"Ok([1, 11, 1426, 3])"`', exercises/standard_library_types/iterator3.rs:75:9
And it isn't clear what the left side results are as the iteration and map have not happened. The other various things I have tried provide similar results.
Thinking that the problem is lazy evaluation, I have also tried using collect to see if that will force evaluation. For example, calling collect at the end of the division_results line like this:
division_results = numbers.into_iter().map(|n| divide(n, 27)).collect();
Provides an error:
cannot infer type consider giving `division_results` a type
When I modify collect to say:
let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<i32>();
I get an error that I thought gave me a hint at the right type:
let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<i32>();
| ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=std::result::Result<i32, DivisionError>>`
So I tried with the type shown in the error message:
let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<Result<i32, DivisionError>>();
Only to get this error:
let division_results = numbers.into_iter().map(|n| divide(n, 27)).collect::<Result<i32, DivisionError>>();
| ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=i32>`
Clearly I am missing something. Perhaps you can tell me what?
Upvotes: 4
Views: 696
Reputation: 602635
The map()
method on an iterator adapter; it takes an iterator, and returns another iterator, but it does not consume any items from the original iterator by itself. The return type Map
is a wrapper around the original iterator that applies the provided closure to every item when they are consumed.
If you want the Map
to actually do something, you need to consume the iterator. The most common ways to do so are for
loops and the collect()
method (but there are many other methods that consume the iterator, like sum()
, count()
, fold()
, max()
, …). In this particular case, calling the collect()
method is most appropriate, since you want to collect the results in a vector.
You already figured out that the desired type for x
is a Result
wrapping a vector of i32
or an error, or Result<Vec<i32>, DivisionError>
in Rust syntax. Since collect()
can produce many different return types, we need to tell the compiler which one we want. One way to do so is to explicitly specify the type of x
:
let x: Result<Vec<i32>, DivisionError> = division_results.collect();
The other case you mentioned is very similar. This time, the target type is a vector of Result
instances, so all you need to do is specify the different type. This will automatically select the right implementation of FromIterator
for you:
let x: Vec<Result<i32, DivisionError>> = division_results.collect();
Upvotes: 7