For example, the following code doesn't compile. Is there any reason behind this?
let x = 1;
match x {
0 + 1 => "how",
_ => "are",
}
For example, the following code doesn't compile. Is there any reason behind this?
let x = 1;
match x {
0 + 1 => "how",
_ => "are",
}
Expression evaluation is undecidable, so there would be no way for the compiler to enforce exhaustive (or even terminating) matches.
The following if/else
code block works. So besides simpler syntax, what is the advantage of match
over if/else
?
let x = 1;
if x == 0 + 1 {
"how"
} else {
"are"
}
For a two-way branch that only uses logical or relational operators, if
is probably more appropriate. match
shines when you need to actually match against complex, nested patterns, and/or extract associated values from enum
s. For example, this:
match some_enum_value {
Variant1(value1) => do_stuff(value1),
Variant2(value2) => do_something(value2),
Variant3(value3) => do_something_else(value3),
}
is less verbose than
if let Variant1(value1) = some_enum_value {
do_stuff(value1)
} else if let Variant2(value2) = some_enum_value {
do_something(value2)
} else if let Variant3(value3) = some_enum_value {
do_something_else(value3)
}
And enormously better than
if let Variant1(value1) = some_enum_value {
do_stuff(value1)
} else if let Variant2(value2) = some_enum_value {
do_something(value2)
} else if let Variant3(value3) = some_enum_value {
do_something_else(value3)
} else {
println!("This branch should not be reached. Ask devs if you found this log printed");
}
Why does the following code works then? The value of y
is not determined at compile time, right?
let x = 1;
fn f(x: i32) -> i32 {
x
}
let y = f(1);
match x {
y => "how",
_ => "are",
}
If you compile this code, you'll see various compile warnings.
Compiling playground v0.0.1 (/playground)
warning: unreachable pattern
--> src/main.rs:9:9
|
8 | y => "how",
| - matches any value
9 | _ => "are",
| ^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: unused variable: `y`
--> src/main.rs:6:9
|
6 | let y = f(1);
| ^ help: if this is intentional, prefix it with an underscore: `_y`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `y`
--> src/main.rs:8:9
|
8 | y => "how",
| ^ help: if this is intentional, prefix it with an underscore: `_y`
warning: 3 warnings emitted
Finished dev [unoptimized + debuginfo] target(s) in 1.55s
Running `target/debug/playground`
The _ =>
arm is unreachable, the let y
variable is unused, and another variable y
in the y =>
branch also is unused.
The left side of the =>
in the match expression is pattern. y
is an identifier pattern which is irrefutable pattern and creates a variable holding the matched value.
match x {
y => "how",
_ => "are",
}
In this match expression the value x
is first matched over the y
pattern. It's irrefutable means it always succeed, the value of the x
is now bind to the new variable y
and its arm "how"
is evaluated.
I see. match
does pattern matching rather than value comparison. It is not equivalent to the C-like switch statement. However, if I have to do multiple value comparisons (can only be calculated using expressions), is there a simple way rather than using if
statement?
Yes, you can do that using "match guards":
match x {
val if val == 0 + 1 => { ... },
val if val == f(3) => { ... },
val => { ... },
}
That's cool! But it is even more verbose compared to an if/else
statement.
Why don't you just use an if
, then?
Only for such a simple example. If you're matching as well, it works great:
match x {
Some(val) if val == 0 + 1 => { ... },
Some(val) if val == f(3) => { ... },
Some(val) => { ... },
None => { ... },
}
It also saves you from changing indentation, which makes the scope clearer.
This is supported on nightly using const blocks:
let x = 1;
match x {
const { 0 + 1 } => "how",
_ => "are",
}
It is syntactically disallowed because or-patterns, struct patterns, and a few other things are ambiguous between expressions and patterns. The const
unambiguously signifies that the contents should be parsed as an expression and any variables inside it should be coming from the surroundings, and are not new variable bindings.
You can also do this on stable but you need an auxiliary const
definition:
let x = 1;
const Z: i32 = 0 + 1;
match x {
Z => "how",
_ => "are",
}
How about:
match "whatever" {
_ if a == b + c => "Yes",
_ if b == c - a => "No",
_ if c == a * b => "May be",
...
_ => "WTF",
};
Saves all those pesky } else {
.
I thought Z
matches any pattern.
If it is a constant in scope, it resolves to the constant. Otherwise it is treated like a variable name. The relevant section from the reference is identifier patterns:
Path patterns take precedence over identifier patterns. It is an error if
ref
orref mut
is specified and the identifier shadows a constant.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.