Why is a type assertion needed after collect()?

I have code like this, and I want to get a Vec<String> from Vec<&str>:

        let data = vec![
            "this", "is", "a", "sentence", "that", "is", "not", "a", "sentence",
        ]
        .into_iter()
        .map(String::from)
        .collect();

And this will give me an error like this:

error[E0277]: a value of type `&[String]` cannot be built from an iterator over elements of type `String

But it is fine if I assert it like this:

        let data: Vec<String> = vec![
            "this", "is", "a", "sentence", "that", "is", "not", "a", "sentence",
        ]
        .into_iter()
        .map(String::from)
        .collect();

Why can't Rust pick up the change of type there? Is there something happened under the hood that manual assertion is needed?

Because at the moment that you run the into_iter method you no longer have a vector of strings or string slices, you have an iterator, and that iterator can then be "collected" into several different data structures such as another vector, an array, a map, etc.

6 Likes

It could "collect" in any struct implementing FromIterator. You have to hint at which one.

let list: Vex<_> = ...

Would be enough in your case.

The previous posts are correct, but to elaborate,

You could also start with an array instead of a Vec-via-vec!:

    let data: Vec<String> = [
            "this", "is", "a", "sentence", "that", "is", "not", "a", "sentence",
        ]
        .into_iter()
        .map(String::from)
        .collect();

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=96b152ac08ad38da7db85e563035233e

Or you could collect into something other than a Vec, perhaps a HashSet:

    let data: std::collections::HashSet<String> = vec![
            "this", "is", "a", "sentence", "that", "is", "not", "a", "sentence",
        ]
        .into_iter()
        .map(String::from)
        .collect();

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4dab9493e3b102ed69bc33e6ec74f642

4 Likes

The error message

error[E0277]: a value of type `&[String]` cannot be built from an iterator over elements of type `String`

is strange, though. Help says: You tried to use a type which doesn't implement some trait in a place which expected that trait.

Here, type inference doesn't have a goal type, because the "let" is untyped. The error message indicates that a goal type is known, but there's no way to get there. This seems to indicate that the type inference system did guess at the type of data, but made a bad guess. That isn't supposed to happen with a Hindler-Miner algorithm, which doesn't guess. This looks more like a heuristic along the lines of "if collecting T, assume the goal type is [T]". Something decided on a goal type of &[String].

An error message such as "Not enough information to infer the type of data" would seem more appropriate.

Well, OP probably passed data somewhere further, and this usage made this value typed as &[String]. We can't know without reproducible example, of course.

3 Likes