Daniel Butler
Daniel Butler

Reputation: 3726

for loop with `.collect()` cannot infer type

When running the following code, which comes from attempting the exercise in the Rust Book:

use std::collections::HashMap;

fn main() {
    let mut values = [5, 34, 31, 4, 31, 25, 28, 45, 43, 14];
    values.sort();

    let mut total = 0;
    let mut mode_collection = HashMap::new();
    let mut mode = HashMap::new();

    for value in values.iter() {
        let count = mode_collection.entry(value.clone()).or_insert(0);
        *count += 1;
        total += value;
    };

    for (value, count) in mode_collection.iter() {
        let count_values = mode.entry(count).or_insert(vec![]);
        count_values.push(value.clone());
    }

    let mut largest_count: i32 = 0;
    for count in mode.keys().collect() {
        if count > largest_count {
            largest_count = count;
        }
    }

    println!("The average of the values is {}", total / values.len());
    println!("The median of the values is {}", values[values.len() / 2]);
    println!("The mode of the values is {:?}", mode[&largest_count]);
}

In Rust Playground

I get an error:

error[E0282]: type annotations needed
  --> src\main.rs:24:18
   |
24 |     for count in mode.keys().collect() {
   |                  ^^^^^^^^^^^^^^^^^^^^^ cannot infer type

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.
error: could not compile `enums`

To learn more, run the command again with --verbose.

How can I avoid this error?

As best I can tell, type annotations cannot be added to for loops. However type annotations are required when using collect(). When I get rid of collect() count is a &&{Integer} (a double borrowed integer?) so the largest_count and count variable can't be compared.

Upvotes: 1

Views: 2000

Answers (2)

Dmitry
Dmitry

Reputation: 1637

  1. If you need an explicit type annotation, you can use next semantics:

    for count in mode.keys().collect::<Vec<_>>() {
       // do stuff over each count
    }
    
  2. You don't need to collect items to iterate over them in for cycle. mode.keys() is already an iterator, so you can just write:

    for count in mode.keys() {
        // do stuff
    }
    
  3. If you need to get some single result from an iterator, it is better to use fold:

    let largest_count = mode.keys().copied().fold(0, |largest, current| largest.max(current));
    
  4. In your particular case you can just write:

    let largets_count = mode.keys().copied().max();
    

Upvotes: 3

sebpuetz
sebpuetz

Reputation: 2618

You're running into two issues here:

  1. The collect() fails because it's impossible to know what type the resulting collection should have. You can annotate the type through the turbo-fish as mode.keys().collect::<Vec<_>>(). This won't fix your issue since it doesn't change the type of the items being collected.
  2. You can simply dereference the integers in the loop. This will fix your issue and does not require collecting at all. I.e. **count turns an &&i32 into an i32 and allows you to compare it as expected.

You might also want to rethink storing references to integers in the hashmap, then you could solve the issue with a copied() call on the keys() iterator.

Upvotes: 2

Related Questions