In Rust, matching a value like this works:
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
_ => println!("something else")
}
But using values from a vector instead of hard-coded numbers in match doesn't work:
let x = 1;
let list = vec![1, 2];
match x {
list[0] => println!("one"),
list[1] => println!("two"),
_ => println!("something else")
}
This fails with the message:
error: expected one of `=>`, `#`, `if`, or `|`, found `[`
--> src/main.rs:6:9
|
6 | list[0] => println!("one"),
| ^ expected one of `=>`, `#`, `if`, or `|` here
Why doesn't it work?
The pattern of a match arm is defined as
Syntax
Pattern :
LiteralPattern
| IdentifierPattern
| WildcardPattern
| RangePattern
| ReferencePattern
| StructPattern
| TupleStructPattern
| TuplePattern
| GroupedPattern
| SlicePattern
| PathPattern
| MacroInvocation
It's either constant (including literal) or structural, not computed. A value defined as list[0] matches none of those definitions.
Fortunately, a match arm may also contain a guard expression, which allows for this:
let x = 1;
let list = vec![1, 2];
match x {
_ if x == list[0] => println!("one"),
_ if x == list[1] => println!("two"),
_ => println!("something else")
}
Using if else would be cleaner, though (or a different structure if you have more cases, like a map, or the index).
Related
The following code (playground)
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
max_column => Edge::Right,
_ => Edge::NotAnEdge
};
results in the following warning:
warning: unreachable pattern
--> src/main.rs:10:9
|
9 | max_column => Edge::Right,
| ---------- matches any value
10 | _ => Edge::NotAnEdge
| ^ unreachable pattern
|
= note: #[warn(unreachable_patterns)] on by default
Replacing the variable max_column with the literal works fine:
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
7 => Edge::Right,
_ => Edge::NotAnEdge
};
Why is _ unreachable in the first example when it can be reached for any values where current_column != max_column?
The Rust Programming Language explains how a match expression is processed, emphasis mine:
When the match expression executes, it compares the resulting value against the pattern of each arm, in order.
In your example, max_column is the name of the variable to be bound to, not a constant or an outside variable. When the compiler reaches max_column, any remaining values will be assigned to that match arm, making subsequent arms unreachable.
In your case, you want to make max_column a real constant:
let current_column = 1;
const MAX_COLUMN: i32 = 7;
edge = match current_column {
0 => Edge::Left,
MAX_COLUMN => Edge::Right,
_ => Edge::NotAnEdge
};
Or if that's not possible, you want a match guard:
let current_column = 1;
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
a if a == max_column => Edge::Right,
_ => Edge::NotAnEdge
};
Note that, as a first approximation, a and _ are the same thing in this case! In both cases, the matched variable will be bound to a name (a or _ respectively), but any identifier prefixed with _ is special-cased to be used as an unused variable placeholder.
bluss clarifies and corrects this approximation:
_ is a separate special case, it's not a variable binding at all, but it is the absence of one! Matching against _x moves the value into _x, _ does no such thing. (The difference is observable.)
The following code (playground)
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
max_column => Edge::Right,
_ => Edge::NotAnEdge
};
results in the following warning:
warning: unreachable pattern
--> src/main.rs:10:9
|
9 | max_column => Edge::Right,
| ---------- matches any value
10 | _ => Edge::NotAnEdge
| ^ unreachable pattern
|
= note: #[warn(unreachable_patterns)] on by default
Replacing the variable max_column with the literal works fine:
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
7 => Edge::Right,
_ => Edge::NotAnEdge
};
Why is _ unreachable in the first example when it can be reached for any values where current_column != max_column?
The Rust Programming Language explains how a match expression is processed, emphasis mine:
When the match expression executes, it compares the resulting value against the pattern of each arm, in order.
In your example, max_column is the name of the variable to be bound to, not a constant or an outside variable. When the compiler reaches max_column, any remaining values will be assigned to that match arm, making subsequent arms unreachable.
In your case, you want to make max_column a real constant:
let current_column = 1;
const MAX_COLUMN: i32 = 7;
edge = match current_column {
0 => Edge::Left,
MAX_COLUMN => Edge::Right,
_ => Edge::NotAnEdge
};
Or if that's not possible, you want a match guard:
let current_column = 1;
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
a if a == max_column => Edge::Right,
_ => Edge::NotAnEdge
};
Note that, as a first approximation, a and _ are the same thing in this case! In both cases, the matched variable will be bound to a name (a or _ respectively), but any identifier prefixed with _ is special-cased to be used as an unused variable placeholder.
bluss clarifies and corrects this approximation:
_ is a separate special case, it's not a variable binding at all, but it is the absence of one! Matching against _x moves the value into _x, _ does no such thing. (The difference is observable.)
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;
The following code (playground)
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
max_column => Edge::Right,
_ => Edge::NotAnEdge
};
results in the following warning:
warning: unreachable pattern
--> src/main.rs:10:9
|
9 | max_column => Edge::Right,
| ---------- matches any value
10 | _ => Edge::NotAnEdge
| ^ unreachable pattern
|
= note: #[warn(unreachable_patterns)] on by default
Replacing the variable max_column with the literal works fine:
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
7 => Edge::Right,
_ => Edge::NotAnEdge
};
Why is _ unreachable in the first example when it can be reached for any values where current_column != max_column?
The Rust Programming Language explains how a match expression is processed, emphasis mine:
When the match expression executes, it compares the resulting value against the pattern of each arm, in order.
In your example, max_column is the name of the variable to be bound to, not a constant or an outside variable. When the compiler reaches max_column, any remaining values will be assigned to that match arm, making subsequent arms unreachable.
In your case, you want to make max_column a real constant:
let current_column = 1;
const MAX_COLUMN: i32 = 7;
edge = match current_column {
0 => Edge::Left,
MAX_COLUMN => Edge::Right,
_ => Edge::NotAnEdge
};
Or if that's not possible, you want a match guard:
let current_column = 1;
let max_column = 7;
edge = match current_column {
0 => Edge::Left,
a if a == max_column => Edge::Right,
_ => Edge::NotAnEdge
};
Note that, as a first approximation, a and _ are the same thing in this case! In both cases, the matched variable will be bound to a name (a or _ respectively), but any identifier prefixed with _ is special-cased to be used as an unused variable placeholder.
bluss clarifies and corrects this approximation:
_ is a separate special case, it's not a variable binding at all, but it is the absence of one! Matching against _x moves the value into _x, _ does no such thing. (The difference is observable.)
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;