Hi, I am new to rust I need help understanding the iterator3.rs code in rustlang exercise.
#[test]
fn result_with_list() {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let x: Result<Vec<i32>, DivisionError> = division_results.collect();
assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");
}
#[test]
fn list_of_results() {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27));
let x: Vec<Result<i32, DivisionError>>= division_results.collect();
assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");
}
How does the collect function turn the Result into [Ok(1), Ok(11), Ok(1426), Ok(3)] or Ok([1, 11, 1426, 3])? In haskell you have special functions to turn the Monads inside out. I don't understand how it works here.
For example
let x: Result<Vec, DivisionError> = division_results.collect();
and this line
let x: Vec<Result<i32, DivisionError>>= division_results.collect();
both return different result
The collect method uses the FromIterator trait. The standard library contains this implementation of FromIterator for Result, which returns Err(..) if any item of the iterator is an error, or Ok(..) otherwise.
impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E>
where
V: FromIterator<A> { ... }
Thanks for the answer. But my question is how does the collect know which of the FromIterator to implement
for example this line
let x: Result<Vec, DivisionError> = division_results.collect();
and this line
let x: Vec<Result<i32, DivisionError>>= division_results.collect();
collect is a generic function that uses FromIterator, and FromIterator itself has many generic implementations. There are a lot of options for which implementation to use, so the answer to your question is that sometimes, collectdoesn't know which implementation to use:
Because collect() is so general, it can cause problems with type inference.
So often times, you help tell collect which implementation to use by providing an explicit type:
let x: Result<Vec, DivisionError> = division_results.collect();
let x: Vec<Result<i32, DivisionError>>= division_results.collect();
The x: Result<Vec, DivisionError> and x: Vec<Result<i32, DivisionError>> are how you help collect understand which implementation to use.
You can also provide the type on the right side if preferred:
let x = division_results.collect::<Result<Vec, DivisionError>>();
let x = division_results.collect::<Vec<Result<i32, DivisionError>>>();
This syntax is known at the 'turbofish' and is used here to specify a concrete type to a generic function. See the Examples section of collect's documentation for more examples
So in a way, it's less about whether it's on the left or right side, and more about whether the compiler has enough information to pick a specific type.
The following example shows that you don't have to provide a type on the left or the right side: Playground
We don't explicitly provide a type anywhere for the call to collect, but rust knows that the type of hellomust be String because hello is later used in a function that takes a String. If you remove the line take_string(hello), this will no longer compile and you'll need to provide a type, either on the left or the right, like before.
To resolve the ambiguity, the compiler attempts to determine the target type from local context. In an assignment the place expression on the left side is the determiner of the ultimate target type.