Why result of vec[index] "type annotation needed"?

I am learning leetcode use rustlang, I tried this code:


pub fn group_anagrams1(strs: Vec<String>) -> Vec<Vec<String>> {
    let mut result: Vec<Vec<String>> = vec![];
    let mut m = std::collections::HashMap::new();
    for s in strs {
        let mut cs: Vec<u8> = s.clone().into_bytes().to_vec();
        cs.sort();
        let key = String::from_utf8(cs).unwrap();
        if m.contains_key(&key) {
            let &v = m.get(&key).unwrap();
            let tmp = &mut result[v];
            tmp.push(s);
        } else {
            m.insert(key, result.len());
            result.push(vec![s]);
        }
    }
    result
}

this happened:

18 |             let tmp = &mut result[v];
   |                 --- consider giving `tmp` the explicit type `&mut _`, with the type parameters specified
19 |             tmp.push(s);
   |                 ^^^^ cannot infer type

why tmp need explicit type?

Meta

rustc --version --verbose:

rustc 1.53.0 (53cb7b09b 2021-06-17)
binary: rustc
commit-hash: 53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b
commit-date: 2021-06-17
host: x86_64-apple-darwin
release: 1.53.0
LLVM version: 12.0.1

The compiler is having a hard time figuring out the type of m, and therefore v and result[v]. You can get around it by adding a type annotation here:

result[v as usize].push(s);

or here:

let mut m: HashMap<String, usize> = HashMap::new();

On a side note, you can write the lookup like this to get rid of the unwrap:

if let Some(&v) = m.get(&key) {
  result[v].push(s);
} else {
  m.insert(key, result.len());
  result.push(vec![s]);
}

thx for answer

I'm confused, compiler should infer type of m

m.insert(key, result.len());

It decides it needs to know before then and doesn't look that far ahead... or such. If you reverse the blocks:

        // `!` is new, block contents are swapped
        if !m.contains_key(&key) {
            m.insert(key, result.len());
            result.push(vec![s]);
        } else {
            let &v = m.get(&key).unwrap();
            let tmp = &mut result[v];
            tmp.push(s);
        }

it infers correctly. Maybe someday it will cover your original use case.

You could also use the entry API:

pub fn group_anagrams1(strs: Vec<String>) -> Vec<Vec<String>> {
    let mut result: Vec<Vec<String>> = vec![];
    let mut m = std::collections::HashMap::new();
    for s in strs {
        let mut cs: Vec<u8> = s.clone().into_bytes().to_vec();
        cs.sort();
        let key = String::from_utf8(cs).unwrap();
        let index_ref = m.entry(key).or_insert_with(|| {
            let i = result.len();
            result.push(Vec::new());
            i
        });

        result[*index_ref].push(s);
    }

    result
}
1 Like