I am fairly new to Rust and cannot get my head around this confusing error.
I am simply trying to match on an Option
returned by the get
function of a HashMap
. If a value is returned I want to increment it, if not I want to add a new element 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),
}
The resulting 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 am not really sure what types the compiler is complaining about here, as both Some
and None
are both part of the same enum type. Can anyone explain what issue the compiler is having with my code?
The compiler is referring to the value the match arm bodies return, not the type of the pattern of each match arm.
Some(count) => *count += 1,
None => map.insert(a, 0),
The expression *count += 1
evaluates to ()
(called "unit" in Rust, "void" in many other languages). The expression map.insert(a, 0)
on the other hand returns Option<V>
where V
is the value type of the hash map (an integer in your case). Suddenly the error message does make some sense:
= note: expected type `()`
= note: found type `std::option::Option<{integer}>`
I suppose you don't even want to return something from the match
block (remember: match
blocks are expressions, too, so you could return something from it). To discard the result of any expression, you can convert it to a statement with ;
. Let's try this:
match map.get(&a) {
Some(count) => {
*count += 1;
}
None => {
map.insert(a, 0);
}
}
Each match arm body is a block now (something between {
and }
) and each block contains one statement. Note that we technically don't need to change the first match arm, as *count += 1
already returns ()
, but this way it's more consistent.
But once you test this, another error related to borrowing will be shown. This is a well known issue and is explained in more detail here. In short: the borrow checker is not smart enough to recognize that your code is fine and therefore you should use the super nice entry
-API:
let map = HashMap::new();
map.insert("a", 0);
let a = "a";
*map.entry(&a).or_insert(0) += 1;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With