Type inference failure / question

Let's say I have a struct like so:

struct Foo { names: Vec<String> }

impl Foo {
    fn parse(data: &str) -> Foo {
        // don't have to specify a type for names
        let names = data.split('|').map(str::to_owned).collect();
        Foo { names : names }
    }
}

Type inference seems to work to figure out that the local variable names is a Vec<String>. But if I try to use names, other than by assigning it into the struct literal, I get an error "the type of this value must be known in this context".

For example, both of the following fail unless I write let names: Vec<_> = ...

fn parse_1(data: &str) -> Foo {
    let names = data.split('|').map(str::to_owned).collect();
    if names.len() < 2 {
        // the error occurs on the call to len
    }
    Foo { names : names }
}

fn parse_2(data: &str) -> Foo {
    let names = data.split('|').map(str::to_owned).collect();
    Foo { names : names.clone() }
    // the error now occurs on the call to clone
    if names.len() < 2 {
        // do something
    }
}

Is this a bug in type inference, or an expected limitation?

edit: nevermind, I misread the code.

Yes, but the problem is not with the .collect() method; in fact, the call to .collect() in the first version I gave of my parse function compiled and worked just fine without the hint.

In other words, assigning a variable created with .collect() to a struct member of type Vec<String> seems to be enough for rustc to know which version of .collect() to use without specifying the type of that variable; but it doesn't seem to be enough to let it know how to handle other uses of that same variable (e.g., names.len() or names.clone()) in the same method.

The obstacle is the .clone(). If you change names.clone() to Clone::clone(&names), type inference works again? Why? In both cases the compiler knows that the return value is of type Vec<String>. But names.clone() can mean different things depending on the type of names; for example, names could have a type that does not implement the Clone trait but has an inherent method named clone that happens to return a Vec<String>. Naming the Clone trait lets the compiler reason based on the declaration of Clone::clone, namely fn clone(&self) -> Self, which is enough to conclude that &names is an &Vec<String>, ergo names is a Vec<String>.

(In fact, Clone::clone(&names) can also work if names has a type other than Vec<String>, due to deref coercions. But the compiler is still okay with inferring Vec<String> in that case.)

1 Like

Thanks! Is the same kind of thing true for my parse_1 example, which does not use .clone() but uses .len()? e.g.

fn parse_1(data: &str) -> Foo {
    let names = data.split('|').map(str::to_owned).collect();
    if names.len() < 2 {
        // the error occurs on the call to len
    }
    Foo { names : names }
}

I've done some experimenting, and I think I understand; type inference works "sequentially." So this compiles:

let foo = "a|b".split('|').map(str::to_owned).collect(); // 1
let bar: Vec<String> = foo;                              // 2

and this compiles (because in line 3, rustc already knows it's a Vec<String> from line 2):

let foo = "a|b".split('|').map(str::to_owned).collect(); // 1
let bar: &Vec<String> = &foo;                            // 2
if foo.len < 2 {}                                        // 3

but this doesn't (because in line 2, rustc does not yet have the information from line 3 to tell it that foo is a Vec<String>).

let foo = "a|b".split('|').map(str::to_owned).collect(); // 1
if foo.len < 2 {}                                        // 2
let bar: &Vec<String> = &foo;                            // 3