Need help in understanding iterator3.rs in rustlang exercise

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> { ... }
1 Like

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, collect doesn'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.

1 Like

Thanks for the reply. It's interesting that it looks on the left side to figure out what implementation to use. I guess that's where I got confused.

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

fn main() {
    let chars = vec!['h', 'e', 'l', 'l', 'o'];
    let hello = chars.iter().collect();
    take_string(hello);
}

fn take_string(s: String) {
    println!("{}", s);
}

We don't explicitly provide a type anywhere for the call to collect, but rust knows that the type of hello must 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.

1 Like

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.

1 Like