I wanna to implement when e type is Notfound create file
let file = File::open("pass.ini");
match file {
Ok(f) => {
todo!()
}
Err(e) => {
if let e = NotFound {
File::create("pass.ini");
}
}
}
but use cargo check,compiler tell me this:
warning: irrefutable `if let` pattern
--> src/main.rs:51:16
|
51 | if let e = NotFound {
| ^^^^^^^^^^^^^^^^
|
= note: `#[warn(irrefutable_let_patterns)]` on by default
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
tl;dr, use if e.kind() == NotFound {
In if let e = NotFound {, the left hand side (e) is the pattern you are matching the right hand side (NotFound) against. As e will match everything, it will always be true. Confusingly, the e in your statement is not the variable that was bound by the e in the match arm.
You could also include the condition in the match arm,
let file = File::open("pass.ini");
match file {
Ok(f) => {
todo!()
}
Err(e) if e.kind() == NotFound => File::create("pass.ini"),
Err(e) => todo!()
}
Related
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
I mean, see code
macro_rules! impls {
() => {
let a = "Hello, world!";
println!("{}", match 7 {
impls!{ 3 + 4, a }
_ => unimplemented!()
})
};
($x:expr, $name:ident) => {
($x) => $name,
}
}
fn main() {
impls!{ }
}
As I think, impls!{ 3 + 4, a } should be (3 + 4) => a, but it's not. What's wrong?
Compiler error:
error: expected one of `=>`, `if`, or `|`, found reserved identifier `_`
--> src/main.rs:6:13
|
5 | impls!{ 3 + 4, a }
| - expected one of `=>`, `if`, or `|`
6 | _ => unimplemented!()
| ^ unexpected token
...
16 | impls!{ }
| --------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
That is not possible with declarative macros (it might be with procedural macros, I've very little experience with those so I do not know): per the documentation
Macros can expand to expressions, statements, items (including traits, impls, and foreign items), types, or patterns.
A match arm is not in and of itself a "Rust construct", it's a sub-item of a match expression (composed of a pattern, an optional guard expression and a body expression).
As a result, it can not be produced by a declarative macro.
While macros cannot expand to match arm, as said, it is still possible to use them for patterns:(playground)
struct A(usize);
struct B(f32);
macro_rules! impls {
() => {
let x = A(3 + 4);
println!("{}", match x {
impls!{ A -> 7 } => "Hello,",
_ => unimplemented!(),
})
};
($e:expr) => {
let x = B(4.0);
println!("{}", match x {
impls!{ B -> x } => if x==$e { "world!" } else {"None"},
})
};
($a:tt -> $x:tt) => { $a($x) };
}
fn main() {
impls!{ }
impls!{ 16f32.sqrt() }
impls!{ }
impls!{ 1.0 }
}
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
I can straight-forwardly match a String in Rust:
let a = "hello".to_string();
match &a[..] {
"hello" => {
println!("Matches hello");
}
_ => panic!(),
}
If I have an option type, it fails:
match Some(a) {
Some("hello") => {
println!("Matches some hello");
}
_ => panic!(),
}
because the types don't match:
error[E0308]: mismatched types
--> src/main.rs:5:14
|
5 | Some("hello") => {
| ^^^^^^^ expected struct `std::string::String`, found reference
|
= note: expected type `std::string::String`
found type `&'static str`
I can't do the [..] trick because we have an Option. The best that
I have come up with so far is:
match Some(a) {
Some(b) => match (&b[..]) {
"hello" => {
println!("Matches some, some hello");
}
_ => panic!(),
},
None => panic!(),
}
which works but is terrible for its verbosity.
In this case, my code is just an example. I do not control the creation of either the String or the Some(String) — so I can't change this type in reality as I could do in my example.
Any other options?
It's a known limitation of Rust's patterns.
Method calls (including internal methods for operators like ==) automatically call .deref() as needed, so String gets automagically turned into &str for comparisons with literals.
On the other hand, the patterns are quite literal in their comparisons, and find that String and &str are different.
There are two solutions:
Change Option<String> to Option<&str> before matching on it: Some(a).as_deref(). The as_deref() is a combo of as_ref() that makes Option<&String> (preventing move), and deref()/as_str() then unambiguously references it as a &str.
Use match guard: match Some(a) { Some(ref s) if s == "hello" => … }. Some(ref s) matches any String, and captures it as s: &String, which you can then compare in the if guard which does the usual flexible coercions to make it work.
See also:
Converting from Option<String> to Option<&str>
As of Rust 1.40, you can now call as_deref on Option<String> to convert it to Option<&str> and then match on it:
match args.nth(1).as_deref() {
Some("help") => {}
Some(s) => {}
None => {}
}
I found this because it is one of the clippy lints.
Look at this.
You cannot match on std::String, as you've found, only on &str. Nested pattern matches work, so if you can match on &str, you can match on Option<&str>, but still not on Option<String>.
In the working example, you turned the std::String into a &str by doing &a[..]. If you want to match on a Option<String>, you have to do the same thing.
One way is to use nested matches:
match a {
Some(ref s) => match &s[..] {
"hello" => /* ... */,
_ => /* ... */,
},
_ => /* ... */,
}
But then you have to duplicate the "otherwise" code if it's the same, and it's generally not as nice.
Instead, you can turn the Option<String> into an Option<&str> and match on this, using the map function. However, map consumes the value it is called on, moving it into the mapping function. This is a problem because you want to reference the string, and you can't do that if you have moved it into the mapping function. You first need to turn the Option<String> into a Option<&String> and map on that.
Thus you end up with a.as_ref().map(|s| /* s is type &String */ &s[..]). You can then match on that.
match os.as_ref().map(|s| &s[..]) {
Some("hello") => println!("It's 'hello'"),
// Leave out this branch if you want to treat other strings and None the same.
Some(_) => println!("It's some other string"),
_ => println!("It's nothing"),
}
In some cases, you can use unwrap_or to replace Option::None with a predefined &str you don't want to handle in any special way.
I used this to handle user inputs:
let args: Vec<String> = env::args().collect();
match args.get(1).unwrap_or(&format!("_")).as_str() {
"new" => {
print!("new");
}
_ => {
print!("unknown string");
}
};
Or to match your code:
let option = Some("hello");
match option.unwrap_or(&format!("unhandled string").as_str()) {
"hello" => {
println!("hello");
}
_ => {
println!("unknown string");
}
};
If the environment variable SOMEVALUE is found, it should be bound to k:
let k = match env::var(SOMEVALUE) {
Ok(val) => {return val},
Err(e) => println!("could not find {}: {}", SOMEVALUE, e),
};
Compiling this code gives the following error
|
11 | Ok(val) => {return val},
| ^^^ expected (), found struct `std::string::String`
|
= note: expected type `()`
found type `std::string::String`
What Victor wrote is correct, but you probably want to actually bind a value to k; in that case you should change your code to:
let k = match env::var(SOMEVALUE) {
Ok(val) => val,
Err(e) => panic!("could not find {}: {}", SOMEVALUE, e),
};
Equivalently, you can use Result::unwrap_or_else:
let k = env::var(SOMEVALUE).unwrap_or_else(|e| {
panic!("could not find {}: {}", SOMEVALUE, e)
});
In Rust, everything is an expression, there are only a few statements. Each expression returns a value and if the expression does not return anything explicitly, it returns a () (unit) value implicitly.
Your match expression returns different types from 2 branches - a std::String from Ok branch and () from Err branch.
To fix this, you must return something from the second branch too:
let k = match env::var(SOMEVALUE) {
Ok(val) => {return val},
Err(e) => {
println!("could not find {}: {}", SOMEVALUE, e);
String::default()
},
};
This returns a value of the same type - std::String.
Another option you can do is following (This just shows how we can use env::var_os to achieve same purpose)
let spdk_dir = match env::var_os("SPDK_DIR") {
Some(val) => val.into_string().unwrap(),
None => panic!("SPDK_DIR is not defined in the environment")
};
Here, we try to read the environment variable SPDK_DIR and if it is not defined, we exit the program.