How to extract value from option (Solving hashmap2 problem from rustlings) [duplicate] - rust

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;

Related

Why does the order of parameters matter when using the matches! macro?

I'm trying to compare a variable of enum type stored in a vector of structs, with a variable of the same type passed into my function as a parameter. So, both enums are stored in variables. However, I'm getting unexpected results. I'm using the matches!() macro for comparison. Can anyone explain this behaviour?
enum Foo {
A,
B,
}
fn main() {
let a = Foo::A;
if matches!(a, Foo::A) { println!("expected") }
if matches!(a, Foo::B) { println!("not expected 1") }
if matches!(Foo::B, a) { println!("not expected 2") }
let b = Foo::B;
if matches!(a, b) { println!("not expected 3") }
}
Output:
expected
not expected 2
not expected 3
The matches! macro is not symmetrical: the first operand is the expression to be tested and the second operand is a pattern to try matching the first operand against. If this order is not followed, the results will likely be unexpected.
While the first two matches! are well formed and do what you expect, the third and fourth one are most likely not what you want.
matches!(Foo::B, a) // test `Foo::B` against pattern `a`
matches!(a, b) // test `a` against pattern `b`
The expression to be tested is the literal value Foo::B, and the pattern is a new identifier, a in the third example and b in the fourth one. Since the pattern is only an identifier, it will match any expression. It does not relate at all to the variables a and b declared beforehand. The code below would still compile even when b did not exist.
let a = Foo::A;
matches!(a, b);
Note also that these if statements would print warnings, because a new variable was created from the pattern, but was not used.
warning: unused variable: `a`
--> src/main.rs:7:25
|
7 | if matches!(Foo::B, a) { println!("not expected 2") }
| ^ help: if this is intentional, prefix it with an underscore: `_a`
|
= note: `#[warn(unused_variables)]` on by default
See also:
Why does match not work while PartialEq/Eq works fine?
Why is this match pattern unreachable when using non-literal patterns?
Thank you to E_net4 the curator for the helpful answer. I can now get the behaviour I expect using the following:
#[derive(PartialEq)]
enum Foo {
A,
B,
}
fn main() {
let a = Foo::A;
let b = Foo::B;
let s = match a {
n if n == b => "a matches b",
n if n == a => "a matches a",
_ => "no match"
};
println!("{}", s);
}
With output:
a matches a

It is possible to generate a match between an enumeration and the content of a vector

What I am trying to do is to define values ​​within an enumeration with different types of data, and then fill those values ​​into a vector.
Then make a match between that vector and that enumeration so that it returns the value that the enumeration contains
#[derive(Debug)]
enum Types {
Int(i32),
Float(f64),
Text(String),
}
fn main() {
let type = vec![Types::Int(3),Types::Float(3.3),Types::Text("Hi".to_string())];
println!("{:?}",&type[0]);
//if let Types::Int(number) = &type[0] {println!("{:?}",number)}
println!("{:?}",Types::Int(3));
match &type {
Types::Int(number) => number,
Typer::Float(float) => float,
_=> None,
}
}
This code cannot compile.
Besides the fact that you reuse the keyword type as a variable name, the return type of your match statement cannot be inferred.
In each of the arms of your match expression, the code returns different types. In the first, i32, in the second, f64, and in the third, an Option variant. This becomes more obvious if you tried to assign the result.
let foo: <????> = match &typ {
Types::Int(number) => number,
Types::Float(float) => float,
_ => None,
};
What type should be inferred for foo? There is no consistent option, since it depends on the value of typ at runtime, and so cannot be used to define its type at compile time.
Luckly, the Rust compiler is awesome, and you see exactly this in an error message (after fixing the other issues).
error[E0308]: `match` arms have incompatible types
--> src/main.rs:16:32
|
14 | / match typ[0] {
15 | | Types::Int(number) => number,
| | ------ this is found to be of type `i32`
16 | | Types::Float(float) => float,
| | ^^^^^ expected `i32`, found `f64`
17 | | _=> None,
18 | | }
| |_____- `match` arms have incompatible types
error: aborting due to previous error

Get first element from HashMap

I have a HashMap and need to get the first element:
type VarIdx = std::collections::HashMap<u16, u8>;
fn get_first_elem(idx: VarIdx) -> u16 {
let it = idx.iter();
let ret = match it.next() {
Some(x) => x,
None => -1,
};
ret
}
fn main() {}
but the code doesn't compile:
error[E0308]: match arms have incompatible types
--> src/main.rs:5:15
|
5 | let ret = match it.next() {
| _______________^
6 | | Some(x) => x,
7 | | None => -1,
8 | | };
| |_____^ expected tuple, found integral variable
|
= note: expected type `(&u16, &u8)`
found type `{integer}`
note: match arm with an incompatible type
--> src/main.rs:7:17
|
7 | None => -1,
| ^^
how can I fix it?
There is no such thing as the "first" item in a HashMap. There are no guarantees about the order in which the values are stored nor the order in which you will iterate over them.
If order is important then perhaps you can switch to a BTreeMap, which preserves order based on the keys.
If you just need to get the first value that you come across, in other words any value, you can do something similar to your original code: create an iterator, just taking the first value:
fn get_first_elem(idx: VarIdx) -> i16 {
match idx.values().next() {
Some(&x) => x as i16,
None => -1,
}
}
The method values() creates an iterator over just the values. The reason for your error is that iter() will create an iterator over pairs of keys and values which is why you got the error "expected tuple".
To make it compile, I had to change a couple of other things: -1 is not a valid u16 value so that had to become i16, and your values are u8 so had to be cast to i16.
As another general commentary, returning -1 to indicate failure is not very "Rusty". This is what Option is for and, given that next() already returns an Option, this is very easy to accomplish:
fn get_first_elem(idx: VarIdx) -> Option<u8> {
idx.values().copied().next()
}
The .copied() is needed in order to convert the &u8 values of the iterator into u8.
HashMap::iter returns an iterator over (&Key, &Value) pairs. What you want is HashMap::values, which produces an iterator that only produces the values of the HashMap.
Note that the order of the values is random. It has nothing to do with the order you put the values in or with the actual value of the values.

Why do I get the error "expected integral variable, found Option" when matching on an integer?

I am trying to use match in Rust. I wrote a function:
fn main() {
let try = 3;
let x = match try {
Some(number) => number,
None => 0,
};
}
But I'm getting the error:
error[E0308]: mismatched types
--> src/main.rs:4:9
|
4 | Some(number) => number,
| ^^^^^^^^^^^^ expected integral variable, found enum `std::option::Option`
|
= note: expected type `{integer}`
found type `std::option::Option<_>`
error[E0308]: mismatched types
--> src/main.rs:5:9
|
5 | None => 0,
| ^^^^ expected integral variable, found enum `std::option::Option`
|
= note: expected type `{integer}`
found type `std::option::Option<_>`
I tried something like let try: i32 = 3; to make sure that try is an integral value, but I still get the same error.
I think you want this:
fn main() {
let try = Some(3);
let x = match try {
Some(number) => number,
None => 0,
};
}
The issue is that you're trying to match an integer against Some(...) and None, which are Options. This doesn't really make sense... an integer can never be None.
Instead, I think you want to use the type Option<i32> and convert it to an i32 by using a default value. The above code should accomplish that. Note that if that's all you're trying to do, this is an easier way:
let x = try.unwrap_or(0);
In match expressions the type of the value you are matching on must correspond to the variants in the block following it; in your case this means that try either needs to be an Option or the match block needs to have integral variants.
I highly recommend reading The Rust Book; Rust is strongly typed and this is one of the most basic concepts you will need to familiarize yourself with.

Error thrown citing match arms with incompatible types when pattern matching an Option

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;

Resources