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.)
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.)
This question already has an answer here:
How can I store a pattern in a variable in Rust?
(1 answer)
Closed 3 years ago.
This works:
match 0 {
0..=9 => (),
_ => (),
}
But this doesn't:
const R: std::ops::RangeInclusive<u8> = 0..=9;
fn main() {
match 0 {
R => (),
_ => (),
}
}
playground.
If I want to use a range with match, must I use a literal range?
I thinnk the problem is that when you match an u8, each match arm must offer values of type u8 that can be compared to the parameter.
Now, if you write 0..=9, this is not a RangeInclusive (even if it looks the same) - it is a range pattern that can be used to describe a range of values in a match arm.
So, if you write R => () in a match arm, the compiler complains (imo rightfully):
error[E0308]: mismatched types
--> src/main.rs:7:9
|
7 | R => (),
| ^ expected integer, found struct `std::ops::RangeInclusive`
|
= note: expected type `u8`
found type `std::ops::RangeInclusive<u8>`
I.e. it expects a u8 (or, implicitly, a range pattern for u8 values), but it found a RangeInclusive.
Now, one possible solution could be to define the lower and upper bound as separate constants:
const LOWER : u8 = 0;
const UPPER : u8 = 9;
const R: std::ops::RangeInclusive<u8> = LOWER..=UPPER; // <- this is a RangeInclusive<u8>
fn main() {
match 0 {
LOWER..=UPPER => (), // <- this is a range pattern
_ => (),
}
}
Another, in my opinion less appealing, would be a macro that just expands to your range:
macro_rules! myrng{() => {0..=9}}
const R: std::ops::RangeInclusive<u8> = myrng!();
fn main() {
match 0 {
myrng!() => (),
_ => (),
}
}
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 want to compare a value to constants, or other values. In another language (e.g JavaScript) I would do:
// Pretend these are values that are generated another way
let a = 23;
let b = 16;
let c = 43;
let input = 23;
switch (input) {
case a: console.log("Input is equal to a"); break;
case b: console.log("Input is equal to b"); break;
case c: console.log("Input is equal to c"); break;
default: console.log("Input does not equal any value"); break;
}
How would I do this neatly in Rust? I know I could do it with if statements, but I think this is messy and I am comparing to many more values.
Can I compare a variable to constant values in Rust using a match statement?
The simplest way to do this sort of case analysis is when you know the values of the cases beforehand and don't mind having them in the middle of the code. In this case, a simple match expression is what you want.
fn main() {
for &input in &[16, 23, 42, 43] {
match input {
23 => println!("Input is equal to a"),
16 => println!("Input is equal to b"),
43 => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
If your a b and c are compile-time constants (either a known value or generated with const functions), then you can still directly match on them.
const A: i32 = 23;
const B: i32 = 16;
const C: i32 = generate_c();
const fn generate_c() -> i32 {
A + B + 4
}
fn main() {
for &input in &[16, 23, 42, 43] {
match input {
A => println!("Input is equal to a"),
B => println!("Input is equal to b"),
C => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
However, if you try this with non-constant variables, you'll get weird outputs.
fn generate_c(a: i32, b: i32) -> i32 {
a + b + 4
}
fn main() {
let a = 23;
let b = 16;
let c = generate_c(a, b);
for &input in &[16, 23, 42, 43] {
match input {
a => println!("Input is equal to a"),
b => println!("Input is equal to b"),
c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
If you run this, the compiler will give you lots of warnings about "unreachable patterns" and the output will be "Input is equal to a" all four times. The problem with this is that the left side of each line in a match statement is not simply an expression, but rather a pattern.
A pattern is an expression like (x, [_, z], Some(_)). It's built up from basic variables (like x and z), underscores (_), all the literal expressions (integers, floats, tuples, arrays) and a few other things.
When Rust runs a match statement like this, it tries to syntactically match the input with the pattern. Basic variables will match anything and the value of that variable is set to whatever it matched for the scope of that branch of the match statement. Underscores (as used in all of the above examples) match anything too, but don't bind any variables.
With the const version above, the constants A B and C are replaced with their respective literal values everywhere in the code, so the input is matched on those literal values.
With the variable version, when we match with a b and c, these letters are interpreted as basic variables that match anything. The values of the variables aren't considered at all in the pattern. In the code
let a = 14;
let b = 15;
let c = 16;
let input = 16;
match input {
a => println!("Input is equal to a"),
b => println!("Input is equal to b"),
c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
the first branch will always match, giving the input the name a for the scope of the branch.
If you need to match on variables a b and c, you could add a guard to each branch. A guard filters the branch a little more by adding an additional condition for a branch to match. In this case, we match anything and bind it to the variable x, but then check that x is equal to a (and b and c).
fn generate_c(a: i32, b: i32) -> i32 {
a + b + 4
}
fn main() {
let a = 23;
let b = 16;
let c = generate_c(a, b);
for &input in &[16, 23, 42, 43] {
match input {
x if x == a => println!("Input is equal to a"),
x if x == b => println!("Input is equal to b"),
x if x == c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
This is a little bit more verbose than the switch/case construction, but I hope it's clear what's going on. At each branch, the variable x is bound to 16, then if that's equal to the variable A (or B or C), then that branch is taken. Otherwise, we try the next branch.
Can I compare a variable to constant values in Rust using a match statement?
I wrote a macro to (almost) do this. It expands to a match with if guards.
switch! { input;
a => println!("Input is equal to a"),
b => println!("Input is equal to b"),
c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
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).