David Dias
David Dias

Reputation: 1832

Using Rust hashmaps - .find() is not returning the expected value for match

I want to build a function that counts the number of times an element appears in an array, but I'm being unable to resolve a compile time bug, here is my code

fn vec_count_elem(vec: Vec<int>) -> Vec<int> {
    let mut counts: HashMap<int, int> = HashMap::new();;
    let mut result: Vec<int>;

    for el in vec.iter() {
        match counts.find(el) {
            Some(count) => {
                    let new_count: int = count + 1;
                    counts.remove(el);
                    counts.insert(*el, new_count)
                }, 
            None => counts.insert(*el, 0)
        }
    }
    for value in counts.values() {
        result.push(*value);
    }

    return result;   
}

and here is the compile log

/sorting/src/main.rs:40:9: 47:10 error: mismatched types: expected `()`, found `bool` (expected (), found bool)
/sorting/src/main.rs:40         match counts.find(el) {
/sorting/src/main.rs:41             Some(count) => {
/sorting/src/main.rs:42                     let new_count: int = count + 1;
/sorting/src/main.rs:43                     counts.remove(el);
/sorting/src/main.rs:44                     counts.insert(*el, new_count)
/sorting/src/main.rs:45                 },

Looking at this example (http://doc.rust-lang.org/std/collections/struct.HashMap.html#example), the counts.find(el) should return the right data type for the match operator

Thank you!

UPDATE1: The first issue is solved (missing ;), thank you Arjan! Now my problem resides with my access inside the match clause to the counts hashmap, I'm getting this error:

sorting/src/main.rs:50:21: 50:27 error: cannot borrow `counts` as mutable because it is also borrowed as immutable
sorting/src/main.rs:50                     counts.remove(el);
                                                                                                                                                               ^~~~~~
sorting/src/main.rs:46:20: 46:26 note: previous borrow of `counts` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `counts` until the borrow ends

Any ideas what is the best way to do this?

Upvotes: 0

Views: 1028

Answers (3)

Levans
Levans

Reputation: 14992

Using the .entry() method of HashMap, you can make a pretty compact version of it :

use std::collections::hashmap::{HashMap, Occupied, Vacant};

fn vec_count_elem(vec: Vec<int>) -> Vec<int> {
    let mut counts: HashMap<int, int> = HashMap::new();

    for el in vec.iter() {
        match counts.entry(*el) {
            Occupied(mut e) => { *e.get_mut() += 1; },
            Vacant(mut e) => { e.set(1); }
        }
    }

    return counts.values().map(|i| *i).collect();   
}

Upvotes: 4

Dmitriy Pogretskiy
Dmitriy Pogretskiy

Reputation: 11

You can do the job after match statement, as borrowed counts will be released like this:

fn vec_count_elem(vec: Vec<int>) -> Vec<int> {
    let mut counts: HashMap<int, int> = HashMap::new();;
    let mut result: Vec<int> = vec![];

    for el in vec.iter() {
        let (el, new_count) = match counts.find(el) {
            Some(count) => {
                let new_count: int = count + 1;
                (*el, new_count)
            }, 
            None => (*el, 0),
        };
        counts.remove(&el);
        counts.insert(el, new_count);
    }

    for value in counts.values() {
        result.push(*value);
    }

    result   
}

Also result should be initialized first.

Upvotes: 0

Arjan
Arjan

Reputation: 21465

The problem here is that you are returning bool from the match and for is expecting an expression of type (). You can solve this by adding ; after the match:

for el in vec.iter() {
    match counts.find(el) {
        // ...
    };
}

But once you fix this there are other problems with your code:

  • result variable is unitialized
  • You can't call remove inside of the match statement because counts is still borrowed because of the call to find

To fix the second problem replace find with find_mut. Unfortunately insertnig in the case of None does not currently work, but this might change in the future:

for el in vec.iter() {
    let found = match counts.find_mut(el) {
        Some(count) => {
            *count = *count + 1;
            true
        }
        None => false
    };
    if !found {
        counts.insert(*el, 0);
    }
}

Simple solution without reconstructing your code is to just replace find with find_copy.

Upvotes: 1

Related Questions