An error related to a match against an incompatible type when a template matches an option

I am new to Rust and cannot get around this confusing error.

I am just trying to do a mapping on the Option

returned function get

HashMap

. If the value is returned, I want to increase it, if I don't want to add a new item to the map.

Here is the code:

let mut map = HashMap::new();
map.insert("a", 0);
let a = "a";
match map.get(&a) {
    Some(count) => *count += 1,
    None => map.insert(a, 0),
}

      

Received error:

error[E0308]: match arms have incompatible types
  --> <anon>:7:5
   |
7  |       match map.get(&a) {
   |  _____^ starting here...
8  | |         Some(count) => *count += 1,
9  | |         None => map.insert(a, 0),
10 | |     }
   | |_____^ ...ending here: expected (), found enum `std::option::Option`
   |
   = note: expected type `()`
              found type `std::option::Option<{integer}>`
note: match arm with an incompatible type
  --> <anon>:9:17
   |
9  |         None => map.insert(a, 0),
   |                 ^^^^^^^^^^^^^^^^

      

I'm not sure what type of compiler is complaining because both Some

, and None

are part of one and the same type of transfers. Can anyone please explain what problem the compiler has with my code?

+3


source to share


1 answer


The compiler refers to the value returned by the sleeve bodies, not the template type of each sleeve of the match.

Some(count) => *count += 1,
None => map.insert(a, 0),

      

The expression *count += 1

evaluates to ()

(called "one" in Rust, "void" in many other languages). Expression map.insert(a, 0)

, on the other hand, returns Option<V>

, where V

is the type of the hashmap value (an integer in your case). Suddenly the error message makes sense:

= note: expected type `()`
= note:    found type `std::option::Option<{integer}>`

      

I suppose you don't even want to return anything from the block match

(remember: match

these are expressions too, so you can return something). To reverse the result of any expression, you can convert it to a statement using ;

. Let's try this:



match map.get(&a) {
    Some(count) => {
        *count += 1;
    }
    None => {
        map.insert(a, 0);
    }
}

      

The body of each sleeve is now a block (something between {

and }

), and each block contains one statement. Note that we technically don't need to change the first match as it *count += 1

already returns ()

, but that this way it is more consistent.


But once you check this, another borrowing related error will be shown. This is a well known issue and is explained in more detail here . In short: the loan checker is not smart enough to admit that your code is fine and therefore you should use the super-good entry

-API

let map = HashMap::new();
map.insert("a", 0);
let a = "a";
*map.entry(&a).or_insert(0) += 1;

      

+3


source