I simplified an example that comes up in learn rust by example - Guards: https://doc.rust-lang.org/rust-by-example/flow_control/match/guard.html
let number: u8 = 4;
match number {
i if i == 0 => println!("Zero"),
}
}
The example above results in a compilation error regarding the matches cases not being comprehensive. I don't know why. The article says "Note that the compiler won't take guard conditions into account when checking if all patterns are covered by the match expression." But the below example doesn't result in compilation error:
match number {
i => println!("Zero"),
}
The difference is I removed the guard. Why did that affect things if the guard isn't supposed to be taken into account?
The doc you cite doesn't say that the guards are not taken into account, it says that the guard conditions are not taken into account. This means that the following will fail to compile even though all possible values for number are covered:
match number {
i if i >= 0 { 1 },
i if i < 0 { 0 },
}
or even this:
match number {
i if true => { 0 },
}
In other words, the fact that there is a guard on a match arm will cause this arm to be ignored completely when checking if the match is complete.
Related
I simplified an example that comes up in learn rust by example - Guards: https://doc.rust-lang.org/rust-by-example/flow_control/match/guard.html
let number: u8 = 4;
match number {
i if i == 0 => println!("Zero"),
}
}
The example above results in a compilation error regarding the matches cases not being comprehensive. I don't know why. The article says "Note that the compiler won't take guard conditions into account when checking if all patterns are covered by the match expression." But the below example doesn't result in compilation error:
match number {
i => println!("Zero"),
}
The difference is I removed the guard. Why did that affect things if the guard isn't supposed to be taken into account?
The doc you cite doesn't say that the guards are not taken into account, it says that the guard conditions are not taken into account. This means that the following will fail to compile even though all possible values for number are covered:
match number {
i if i >= 0 { 1 },
i if i < 0 { 0 },
}
or even this:
match number {
i if true => { 0 },
}
In other words, the fact that there is a guard on a match arm will cause this arm to be ignored completely when checking if the match is complete.
Using ControlFlow in try_fold seems better than Option in this case where one need to use any value.
But it's not very pleasant to use ControlFlow in try_fold because this irrefutable pattern shown below:
let (Break(value) | Continue(value)) =
values
.iter()
.try_fold(0, |previous, ¤t| match (previous) {
0 => Continue(1),
1 => Continue(2),
_ => Break(3),
});
Is there a way to get rid of this irrefutable pattern while using ControlFlow and retrieving the value?
As far as I can tell, refutable patterns can only be tested in match, if let, and while let expressions. To illustrate what I would like to do, consider the use of the => syntax from the match statement in the following context:
let val = get_optional_value();
if val => Some(inner) {
do_something(inner);
}
I could use an if let statement, but a more useful context would be in short closures:
get_optional_value()
.filter(|iv| iv => InnerVariant::VariantA)
.and_then(/* ... */)
As far as I can tell, the only solution to achieving this using pattern matching would be:
get_optional_value()
.filter(|iv| {
if let InnerVariant::VariantA = iv {
true
} else {
false
}
})
.and_then(/* ... */)
There is a similar question that did not get answered but the comments do point to the use of the ? operator that solves a related corner case for std::result::Result.
Rust 1.42 added the matches! macro. matches!(X, Y) returns a boolean indicating whether expression X matches the pattern Y.
In your case, it could be used like this:
get_optional_value()
.filter(|iv| matches!(iv, InnerVariant::VariantA))
.and_then(/* ... */)
This does not let you bind new names, you can only access the "inner" data in an optional if guard.
If I want to match the result of a rand::thread_rng().get_range(1, 3), I need to add a _ value even if I know there is only two possible values:
match rand::thread_rng().gen_range(1, 3) {
1 => println!("1"),
2 => println!("2"),
_ => panic!("never happens")
};
The _ case is useless but required.
I understand the compiler can't guess that gen_range(1, 3) can only return 1 or 2 but is there a way to avoid adding this useless line _ => panic!("never happens") with pattern matching (maybe with some hint to the compiler)? Or do I need to replace the last value (2) by _?
I understand the compiler can't guess that gen_range(1, 3) can only return 1 or 2
That is correct. gen_range returns an i32 in this case, and an i32 may have many more values.
is there a way to avoid adding this useless line _ => panic!("never happens")
As was covered in the comments, unreachable! better expresses your intent than just a plain panic!:
match rand::thread_rng().gen_range(1, 3) {
1 => println!("1"),
2 => println!("2"),
_ => unreachable!(),
};
replace the last value (2) by _?
This would be fine, but the "failure" case when you eventually change the arguments is likely harder to catch:
match rand::thread_rng().gen_range(1, 100) {
1 => println!("1"),
_ => println!("2"), // oops!
};
A version that panics is more likely to blow up obviously during testing. Which you choose is up to you and your risk tolerance.
The only "bullet proof" alternative is to use an enum { One, Two }, but it leads to more boilerplate and just forces the range check to be done slightly sooner
This works, and would be useful if you wanted to have the equivalent of the match multiple times in your code. This consolidates the logic to one location.
I'm a little surprised there isn't a macros 1.1 crate that allows something like #[derive(Rand)] for an enum... but there are some crates that seem to make it easier.
In this specific case, you could also generate a random boolean and just pick 1 or 2:
if rand::thread_rng().gen() {
println!("1")
} else {
println!("2")
}
I've used "match" a little in Rust, and I've searched for a solution to this problem, because I'm sure there must be a solution. In the following example, in the case where the random number generated is not zero, I want the random-number generated to be assigned to the variable i_bal. In other words, instead of assigning "23456" as per my example, I want to assign the actual random number being tested in the match. I know that there may be better ways of solving this, however, I'd like to know the solution using this method.
Example :
let i_bal: i64 = match std::rand::task_rng().gen() {
0 => 1234,
_ => 23456
};
Instead of the wildcard pattern you can use the variable pattern. They both match everything, but with the variable pattern the matched value is bound to the name:
let i_bal: i64 = match std::rand::task_rng().gen() {
0 => 1234,
x => x
};
Why in some cases you'd want to use the wildcard pattern is so that you make it clear you don't need the value, and so that you don't have to pollute the scope with unnecessary variable names
For example if you had a tuple of (high, low, open, close), and only wanted the variables high and low from it you could do:
let (high, low, _, _) = tickerData