Strange matching behaviour in for loop - rust

Rust noob here. I am puzzled as to why I get this strange behaviour when matching the variable over which I am iterating through a range, with the max of that range. Instead of only matching with the last item, it matches everything! Can someone please explain the output and compiler warning I get from this code:
fn main() {
let maxi = 2;
for i in 0..=maxi {
match i {
maxi => {
println!("i={} matched maxi={}",i,maxi);
}
_ => {
println!("i={} matched with _",i);
}
}
}
}
The compiler output is as follows
warning: unreachable pattern
--> se_match_loop.rs:7:15
|
5 | maxi => {
| ---- matches any value
6 | println!("i={} matched maxi={}",i,maxi);
7 | } _ => {
| ^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: 1 warning emitted
When I execute the compiled script, this is the output
i=0 matched maxi=0
i=1 matched maxi=1
i=2 matched maxi=2
But if I declare maxi as a constant (with const instead of let), I get the expected output
i=0 matched _
i=1 matched _
i=2 matched maxi=2

This is documented under Matching Named Variables.
match starts a new scope, variables declared as part of a pattern
inside the match expression will shadow those with the same name
outside the match construct, as is the case with all variables.
For example, the following code
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
}
will print
Matched, y = 5
at the end: x = Some(5), y = 10
So let's consider your first snippet,
fn main() {
let maxi = 2;
for i in 0..=maxi {
match i {
maxi => {
println!("i={} matched maxi={}",i,maxi);
}
_ => {
println!("i={} matched with _",i);
}
}
}
}
The pattern in your match arm introduces a new variable(Remember match started with a new scope) named maxi that will match any value. That's why the rust compiler emits the following warning with your first snippet.
warning: unreachable pattern
--> se_match_loop.rs:7:15
|
5 | maxi => {
| ---- matches any value
6 | println!("i={} matched maxi={}",i,maxi);
7 | } _ => {
| ^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: 1 warning emitted
Now lets consider the second one(with const),
fn main() {
const maxi: i32 = 2;
for i in 0..=maxi {
match i {
maxi => {
println!("i={} matched maxi={}",i,maxi);
}
_ => {
println!("i={} matched with _",i);
}
}
}
}
This will work as expected because constants are inlined wherever they’re used, making using them identical to simply replacing the name of the const with its value

Related

Initialize variable based on enum

I have an enum:
enum DaEnum{
One(u8),
Strang(String),
}
I would like to variable y to be assigned value from enum:
let x = DaEnum::One(1);
let y;
match x{
DaEnum::One(one) => {y = one},
DaEnum::Strang(strang) => {y = strang},
}
println!("Value of y =>{:?}",y);
Here is the error from cargo run:
error[E0308]: mismatched types
--> src\main.rs:33:40
|
30 | let y;
| - expected due to the type of this binding
...
33 | DaEnum::Strang(strang) => {y = strang},
| ^^^^^^ expected `u8`, found struct `String`
Desired case would be when x is 1, y is 1and when x is a String, y would also be a String.
A generic function may help with your case if it's simple enough. You can pick up all the common behavior into one function and invoke it in every match arm. Please be aware of the trait because it must be shared by all your usages.
#[derive(Clone)]
enum DaEnum {
One(u8),
Strang(String),
}
fn main() {
let x = DaEnum::One(1);
fn common_stuff<T: std::fmt::Debug>(v: T) {
// common stuff
println!("Value of y =>{:?}", v);
}
// (1)
match x.clone() {
DaEnum::One(one) => {
let y = one;
// not common stuff
common_stuff(y);
}
DaEnum::Strang(strang) => {
let y = strang;
// not common stuff
common_stuff(y);
}
}
// (2)
match x {
DaEnum::One(one) => common_stuff(one),
DaEnum::Strang(strang) => common_stuff(strang),
}
}

I want to use the condition of the match statement in Rust to do "0..=a+5"

I would like to use Rust's match statement to process variables differently when they are in an arbitrary range and in other cases. In that case, the code would look like this:
// idx is usize variable
// num is usize variabel
let res: Option<f64> = match idx {
1..=num-5 => {
Some(func())
},
_ => None,
};
But I receive an error:
error: expected one of `::`, `=>`, `if`, or `|`, found `-`
--> src/features.rs:34:22
|
34 | 1..=num-5 => Some(func()),
| ^ expected one of `::`, `=>`, `if`, or `|`
You can check the dynamic range over a match guard:
fn main() {
let idx = 6;
let num = 15;
let res = match idx {
n if (1..=num - 5).contains(&n) => Some("foo"),
_ => None,
};
println!("{:?}", res);
}
Playground
Runtime values cannot be referenced in patterns. Also you cannot make operations.
Use const instead
const num :i32 = 19 - 5;
let res: Option<f64> = match idx {
1..=num => {
Some(func())
},
_ => None,
};

How do I make format! return a &str from a conditional expression?

I happened upon this problem where format! creates a temporary value in a pattern that is not anchored to anything, as far as I understand it.
let x = 42;
let category = match x {
0...9 => "Between 0 and 9",
number # 10 => format!("It's a {}!", number).as_str(),
_ if x < 0 => "Negative",
_ => "Something else",
};
println!("{}", category);
In this code, the type of category is a &str, which is satisfied by returning a literal like "Between 0 and 9". If I want to format the matched value to a slice using as_str(), then I get an error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:5:24
|
3 | let category = match x {
| -------- borrow later stored here
4 | 0...9 => "Between 0 and 9",
5 | number # 10 => format!("It's a {}!", number).as_str(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
|
= note: consider using a `let` binding to create a longer lived value
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
I have done some reading, and found people with similar problems, but I couldn't seem to find any solution.
A simple workaround would be to have category be a String instead of a &str, but I don't like the idea of having to put .to_string() on the end of every literal in the pattern, as it's not as clean.
Is there a way to solve the problem, or do I just need to work around it?
This is 90% a duplicate of Return local String as a slice (&str), see that for multiple other solutions.
There's one extra possibility since this is all in one function: You can declare a variable for the String and only set it when you need to allocate. The compiler (obliquely) suggests this:
consider using a let binding to create a longer lived value
fn main() {
let x = 42;
let tmp;
let category = match x {
0...9 => "Between 0 and 9",
number # 10 => {
tmp = format!("It's a {}!", number);
&tmp
}
_ if x < 0 => "Negative",
_ => "Something else",
};
println!("{}", category);
}
This is mostly the same as using a Cow, just handled by the compiler instead of a specific type.
See also:
How can I conditionally provide a default reference without performing unnecessary computation when it isn't used?
Are polymorphic variables allowed?
format! can't return &str because it will always allocate String. What is possible to do is to return a &str from a String, which is what you did in your code.
As the compiler hinted, the created String is immediately dropped after its creation because it went out of the current scope and one way around could be an external variable that is not bounded to the match scope. E.g.:
use std::fmt::Write;
fn main() {
let mut buffer = String::with_capacity(20);
buffer.push_str("It's a ");
let x = 10;
let category = match x {
0...9 => "Between 0 and 9",
number # 10 => {
write!(&mut buffer, "{}", number).unwrap();
buffer.as_str()
}
_ if x < 0 => "Negative",
_ => "Something else",
};
println!("{}", category);
}
If you want an [no_std] environment or don't want to do any dynamic allocation, you can take a look at this limited code snippet:
use core::str;
fn each_digit<F>(mut number: u32, mut f: F)
where
F: FnMut(u8),
{
while number > 0 {
f((number % 10) as u8);
number /= 10;
}
}
fn main() {
const BUFFER_LEN: usize = 20;
let mut buffer = [0u8; BUFFER_LEN];
let x = 12344329;
let category = match x {
0...9 => "Between 0 and 9",
number # 123443219 => {
let mut idx = BUFFER_LEN;
each_digit(number, |digit| {
let ascii = digit + 48;
idx -= 1;
buffer[idx] = ascii;
});
str::from_utf8(&buffer[idx..BUFFER_LEN]).unwrap()
},
_ => "Something else",
};
assert_eq!("123443219", category);
}
In my case How to overcome "temporary value dropped while borrowed" when converting an i32 to &str
I could solve it by moving the call inside the branches
pub fn uidl(&mut self, message_number: Option<i32>) -> POP3Result {
let command = match message_number {
Some(_) => POP3Command::UidlOne,
None => POP3Command::UidlAll,
};
match message_number {
Some(i) => {
// Here the value is not dropped because it is not leaving the scope
self.execute_command(command, Some(arg.to_string().as_str()))
}
// Here I had to duplicate the call
None => self.execute_command(command, None),
}
}
Kind of what is suggested in the error message https://doc.rust-lang.org/error-index.html#E0597

What is a good way to match strings against patterns and extract values?

I am trying get something like this (doesn't work):
match input {
"next" => current_question_number += 1,
"prev" => current_question_number -= 1,
"goto {x}" => current_question_number = x,
// ...
_ => status = "Unknown Command".to_owned()
}
I tried two different versions of Regex:
go_match = regex::Regex::new(r"goto (\d+)?").unwrap();
// ...
match input {
...
x if go_match.is_match(x) => current_question_number = go_match.captures(x).unwrap().get(1).unwrap().as_str().parse().unwrap(),
_ => status = "Unknown Command".to_owned()
}
and
let cmd_match = regex::Regex::new(r"([a-zA-Z]+) (\d+)?").unwrap();
// ...
if let Some(captures) = cmd_match.captures(input.as_ref()) {
let cmd = captures.get(1).unwrap().as_str().to_lowercase();
if let Some(param) = captures.get(2) {
let param = param.as_str().parse().unwrap();
match cmd.as_ref() {
"goto" => current_question_number = param,
}
} else {
match cmd.as_ref() {
"next" => current_question_number += 1,
"prev" => current_question_number -= 1,
}
}
} else {
status = "Unknown Command".to_owned();
}
Both seem like a ridiculously long and and complicated way to do something pretty common, am I missing something?
You can create a master Regex that captures all the interesting components then build a Vec of all the captured pieces. This Vec can then be matched against:
extern crate regex;
use regex::Regex;
fn main() {
let input = "goto 4";
let mut current_question_number = 0;
// Create a regex that matches on the union of all commands
// Each command and argument is captured
// Using the "extended mode" flag to write a nicer Regex
let input_re = Regex::new(
r#"(?x)
(next) |
(prev) |
(goto)\s+(\d+)
"#
).unwrap();
// Execute the Regex
let captures = input_re.captures(input).map(|captures| {
captures
.iter() // All the captured groups
.skip(1) // Skipping the complete match
.flat_map(|c| c) // Ignoring all empty optional matches
.map(|c| c.as_str()) // Grab the original strings
.collect::<Vec<_>>() // Create a vector
});
// Match against the captured values as a slice
match captures.as_ref().map(|c| c.as_slice()) {
Some(["next"]) => current_question_number += 1,
Some(["prev"]) => current_question_number -= 1,
Some(["goto", x]) => {
let x = x.parse().expect("can't parse number");
current_question_number = x;
}
_ => panic!("Unknown Command: {}", input),
}
println!("Now at question {}", current_question_number);
}
You have a mini language for picking questions:
pick the next question
pick the prev question
goto a specific question
If your requirements end here a Regex based solution fits perfectly.
If your DSL may evolve a parser based solution is worth considering.
The parser combinator nom is a powerful tool to build a grammar starting from basic elements.
Your language has these characteristics:
it has three alternatives statements (alt!): next, prev, goto \d+
the most complex statement "goto {number}" is composed of the keyword (tag!) goto in front of (preceded!) a number (digit!).
any numbers of whitespaces (ws!) has to be ignored
These requirements translate in this implementation:
#[macro_use]
extern crate nom;
use nom::{IResult, digit};
use nom::types::CompleteStr;
// we have for now two types of outcome: absolute or relative cursor move
pub enum QMove {
Abs(i32),
Rel(i32)
}
pub fn question_picker(input: CompleteStr) -> IResult<CompleteStr, QMove> {
ws!(input,
alt!(
map!(
tag!("next"),
|_| QMove::Rel(1)
) |
map!(
tag!("prev"),
|_| QMove::Rel(-1)
) |
preceded!(
tag!("goto"),
map!(
digit,
|s| QMove::Abs(std::str::FromStr::from_str(s.0).unwrap())
)
)
)
)
}
fn main() {
let mut current_question_number = 60;
let first_line = "goto 5";
let outcome = question_picker(CompleteStr(first_line));
match outcome {
Ok((_, QMove::Abs(n))) => current_question_number = n,
Ok((_, QMove::Rel(n))) => current_question_number += n,
Err(err) => {panic!("error: {:?}", err)}
}
println!("Now at question {}", current_question_number);
}
You can use str::split for this (playground)
fn run(input: &str) {
let mut toks = input.split(' ').fuse();
let first = toks.next();
let second = toks.next();
match first {
Some("next") => println!("next found"),
Some("prev") => println!("prev found"),
Some("goto") => match second {
Some(num) => println!("found goto with number {}", num),
_ => println!("goto with no parameter"),
},
_ => println!("invalid input {:?}", input),
}
}
fn main() {
run("next");
run("prev");
run("goto 10");
run("this is not valid");
run("goto"); // also not valid but for a different reason
}
will output
next found
prev found
found goto with number 10
invalid input "this is not valid"
goto with no parameter

Referring to matched value in Rust

Suppose I have this code:
fn non_zero_rand() -> i32 {
let x = rand();
match x {
0 => 1,
_ => x,
}
}
Is there a concise way to put the rand() in the match, and then bind it to a value. E.g. something like this:
fn non_zero_rand() -> i32 {
match let x = rand() {
0 => 1,
_ => x,
}
}
Or maybe:
fn non_zero_rand() -> i32 {
match rand() {
0 => 1,
_x => _x,
}
}
A match arm that consists of just an identifier will match any value, declare a variable named as the identifier, and move the value to the variable. For example:
match rand() {
0 => 1,
x => x * 2,
}
A more general way to create a variable and match it is using the # pattern:
match rand() {
0 => 1,
x # _ => x * 2,
}
In this case it is not necessary, but it can come useful when dealing with conditional matches such as ranges:
match code {
None => Empty,
Some(ascii # 0..=127) => Ascii(ascii as u8),
Some(latin1 # 160..=255) => Latin1(latin1 as u8),
_ => Invalid
}
You can bind the pattern to a name:
fn non_zero_rand() -> i32 {
match rand() {
0 => 1, // 0 is a refutable pattern so it only matches when it fits.
x => x, // the pattern is x here,
// which is non refutable, so it matches on everything
// which wasn't matched already before
}
}

Resources