1

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.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Daniel Butler
  • 3,239
  • 2
  • 24
  • 37

2 Answers2

3
  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();
    
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Dmitry
  • 1,426
  • 5
  • 11
  • 1
    Thank you for the help!. To get #4 to work I needed to add an `expect` because an `Option` was returned. This is what I used `let largest_count = mode.keys().copied().max().expect("There are no values!");` This is the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4688bf393e1d57d68a77fe407253affe) – Daniel Butler Mar 30 '21 at 13:58
2

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.

sebpuetz
  • 2,430
  • 1
  • 7
  • 15
  • `**` worked! https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e6b184bd2f1dd6e4d1819174ca823375 – Daniel Butler Mar 30 '21 at 13:52
  • 2
    Or you can dereference in the pattern match: [`for &&count in mode.keys()`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=28d17159cc06c83029d014a76a699ecd) – Jmb Mar 30 '21 at 14:34
  • 1
    Probably preferable since that's a single dereference instead of having to do it at all places. – sebpuetz Mar 30 '21 at 14:45
  • My understanding was `&` would **make** it a borrow/reference, but that seems wrong. Is using `&` in the variable declaration like saying this **is** a borrow/reference? – Daniel Butler Mar 30 '21 at 14:54
  • In that position it's pattern matching, so you're destructuring the doubly referenced value and get to directly access the value – sebpuetz Mar 30 '21 at 15:40
  • That makes sense – Daniel Butler Mar 30 '21 at 19:09