This question already has an answer here:
Why is this match pattern unreachable when using non-literal patterns?
(1 answer)
Closed 7 months ago.
code
io::stdin()
.read_line(&mut symbol)
.expect("Failed to read line");
println!("You entered {symbol}");
let symbol_x: String = format!("{}", ValidMove::X);
let symbol_o: String = format!("{}", ValidMove::O);
match &symbol {
symbol_x => ok = true,
symbol_o => ok = true,
_ => ok = false,
}
The compiler warns that
the 3rd arm is unreachable
symbol_x is unused
symbol_y is unused
why!?!?
You cannot match against variables, therefore your match statement is equivalent to:
match &symbol {
zzz => ok = true,
yyy => ok = true,
_ => ok = false,
}
Related
This question already has answers here:
How can I pull data out of an Option for independent use?
(3 answers)
Closed 7 months ago.
I want to print out the value of sys.host_name()
let hostname = sys.host_name();
println!("{:?}", hostname);
Output: "Some("arch")"
Expected output: "arch"
How can i print the "raw" string?
It appears (not sure what library you are using) sys.host_name() does not return a string, rather an Option<String>, which implies the function could fail (and return None instead of Some("arch").
println! is printing the full type. If you just wish to print the contained string, you need to extract the string from the Option. One possible way:
let possible_hostname = sys.host_name();
// Check if the hostname is Some or None
if let Some(hostname) = possible_hostname {
println!("{:?}", hostname);
}
else {
println!("hostname could not be determined");
}
If you would prefer your program to just panic if the hostname is None, you can "unwrap" the option:
let hostname = sys.host_name().unwrap();
println!("{:?}", hostname);
This question already has answers here:
How can I pattern match against an Option<String>?
(4 answers)
Closed 8 months ago.
Problem description
I'm trying to match option string with match statement
let option_string = Some(String::from("Bob"));
match option_string {
Some("Mike") => false,
Some("Bob") => true,
_ => false,
}
And, obviously, got an error expected struct 'String, found '&str'.
I tried to change it into string cast
Some("Mike".to_string()) => false
// Or
Some(String::from("Mike")) => false
But faced with a different error: 'fn' calls are not allowed in patterns
The only working way is to place Mike into a variable before Some
let mike = String::from("Mike");
// and in match
Some(mike) => true,
Question
There is a more elegant way to match String but not string literals in match cases with Option value?
I found the answer but it doesn't look elegant enough too. But is it only one possible way to not create extra variables or functions?
let mike = String::from("Mike");
// and in match
Some(mike) => true,
This one is actually a misconception, I'm afraid. Variables are not allowed on the left side of a match expression. Having a name on the left side actually creates a new variable that contains the matched content. So the mike variable in your match clause matches everything and then carries the matched String; it is not the same variable as the outer mike variable.
Pay attention to this code example:
fn main() {
let option_string = Some(String::from("Bob"));
// Note how this line gets the compiler warning "unused variable".
// You could leave this line out completely and it would still
// compile.
let mike = String::from("Mike");
let result = match option_string {
Some(mike) => {
println!("Matched 'Mike': {}", mike);
true
}
_ => false,
};
println!("{:?}", result);
}
Matched 'Mike': Bob
true
In general, you can only match against compile time constants. If you want to compare two variables, you have to use if instead.
Solution
That said, your first example is quite easy to fix:
fn main() {
let option_string = Some(String::from("Bob"));
let result = match option_string.as_deref() {
Some("Mike") => false,
Some("Bob") => true,
_ => false,
};
println!("{:?}", result);
}
true
Note the .as_deref(), which borrows an Option<&str> from the Option<String>, making it compatible with the string literal match expressions.
I'm new to rust, and I'm trying to code a program that opens a websocket for 10 seconds, receive the data from it and then stops. The piece of code is the following.
let now = Instant::now();
while let n=now.elapsed().as_secs() < 10 {
let msg = socket.read_message().expect("Error reading message");
let msg = match msg {
tungstenite::Message::Text(s) => { s }
_ => { panic!() }
};
let parsed: serde_json::Value = serde_json::from_str(&msg).expect("Can't parse to JSON");
let price_str=parsed["p"].as_str().unwrap();
let price: f32 = price_str.parse().unwrap();
write!(f,"1 \t").expect("unable to write");
write!(f, "\t\t {} \n", price).expect("unable to write");
println!("{}",n);
}
n becomes false after 10 seconds, but the loop never ends. What I'm doing wrong?
Thanks for your help.
while let n binds the result of the expression now.elapsed().as_secs() < 10 to n. This binding can never fail, thus your loop never exits.
The compiler emits a lint to prevent such mistakes:
warning: irrefutable `while let` pattern
--> src/lib.rs:24:11
|
24 | while let n = now.elapsed().as_secs() < 10 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(irrefutable_let_patterns)]` on by default
= note: this pattern will always match, so the loop will never exit
= help: consider instead using a `loop { ... }` with a `let` inside it
To fix your snippet, you'll need to remove the let n part. Or in a more unusual and rather unidiomatic manner, you can pattern match on the value returned by now.elapsed().as_secs() < 10 through:
while let true = now.elapsed().as_secs() < 10 {
// do your thing
}
If you want access to the loop control variable, you can still bind it to a variable through:
let now = std::time::Instant::now();
while let n # true = now.elapsed().as_secs() < 10 {
println!("loop_control={}", n)
}
As #Jmb mentions in a comment, there is another issue that's not a compiler error: The loop body may block indefinitely, thus rendering the timeout ineffective.
This question already has answers here:
Unable to read file contents to string - Result does not implement any method in scope named `read_to_string`
(2 answers)
How can I get the current time in milliseconds?
(7 answers)
Closed 2 years ago.
I'm using std::time::SystemTime. My goal is to make a struct with a field called timestamp and store the time in seconds.
I saw this example which works correctly:
use std::time::SystemTime;
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
Err(_) => panic!("SystemTime before UNIX EPOCH!"),
}
When I try this code I get an error:
use std::time::SystemTime;
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
println!("{}", n.as_secs());
error[E0599]: no method named `as_secs` found for enum `std::result::Result<std::time::Duration, std::time::SystemTimeError>` in the current scope
--> src/main.rs:5:22
|
5 | println!("{}", n.as_secs());
| ^^^^^^^ method not found in `std::result::Result<std::time::Duration, std::time::SystemTimeError>`
What am I doing wrong?
Read the error:
no method named `...` found for type `Result<...>`
So, we look at Result:
Result is a type that represents either success (Ok) or faliure (Err)
See the std::result module for documentation details.
So, we know that SystemTime::duration_since(&self, _) returns a Result, meaning it could possibly have failed. Reading the docs:
Returns an Err if earlier is later than self, and the error contains how far from self the time is.
So, we just have to unwrap, expect, or match on it to get the possibility of an error out:
use std::time::SystemTime;
// Unwrapping
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.unwrap(); // Will panic if it is not `Ok`.
// Expecting
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
.expect("Invalid time comparison"); // Will panic with error message
// if it is not `Ok`.
// Matching
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
match n {
Ok(x) => { /* Use x */ },
Err(e) => { /* Process Error e */ },
}
// Fallibly Destructuring:
let n = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
if let Ok(x) = n {
/* Use x */
} else {
/* There was an error. */
}
I want to check whether a string starts with some chars:
for line in lines_of_text.split("\n").collect::<Vec<_>>().iter() {
let rendered = match line.char_at(0) {
'#' => {
// Heading
Cyan.paint(*line).to_string()
}
'>' => {
// Quotation
White.paint(*line).to_string()
}
'-' => {
// Inline list
Green.paint(*line).to_string()
}
'`' => {
// Code
White.paint(*line).to_string()
}
_ => (*line).to_string(),
};
println!("{:?}", rendered);
}
I've used char_at, but it reports an error due to its instability.
main.rs:49:29: 49:39 error: use of unstable library feature 'str_char': frequently replaced by the chars() iterator, this method may be removed or possibly renamed in the future; it is normally replaced by chars/char_indices iterators or by getting the first char from a subslice (see issue #27754)
main.rs:49 let rendered = match line.char_at(0) {
^~~~~~~~~~
I'm currently using Rust 1.5
The error message gives useful hints on what to do:
frequently replaced by the chars() iterator, this method may be removed or possibly renamed in the future; it is normally replaced by chars/char_indices iterators or by getting the first char from a subslice (see issue #27754)
We could follow the error text:
for line in lines_of_text.split("\n") {
match line.chars().next() {
Some('#') => println!("Heading"),
Some('>') => println!("Quotation"),
Some('-') => println!("Inline list"),
Some('`') => println!("Code"),
Some(_) => println!("Other"),
None => println!("Empty string"),
};
}
Note that this exposes an error condition you were not handling! What if there was no first character?
We could slice the string and then pattern match on string slices:
for line in lines_of_text.split("\n") {
match &line[..1] {
"#" => println!("Heading"),
">" => println!("Quotation"),
"-" => println!("Inline list"),
"`" => println!("Code"),
_ => println!("Other")
};
}
Slicing a string operates by bytes and thus this will panic if your first character isn't exactly 1 byte (a.k.a. an ASCII character). It will also panic if the string is empty. You can choose to avoid these panics:
for line in lines_of_text.split("\n") {
match line.get(..1) {
Some("#") => println!("Heading"),
Some(">") => println!("Quotation"),
Some("-") => println!("Inline list"),
Some("`") => println!("Code"),
_ => println!("Other"),
};
}
We could use the method that is a direct match to your problem statement, str::starts_with:
for line in lines_of_text.split("\n") {
if line.starts_with('#') { println!("Heading") }
else if line.starts_with('>') { println!("Quotation") }
else if line.starts_with('-') { println!("Inline list") }
else if line.starts_with('`') { println!("Code") }
else { println!("Other") }
}
Note that this solution doesn't panic if the string is empty or if the first character isn't ASCII. I'd probably pick this solution for those reasons. Putting the if bodies on the same line as the if statement is not normal Rust style, but I put it that way to leave it consistent with the other examples. You should look to see how separating them onto different lines looks.
As an aside, you don't need collect::<Vec<_>>().iter(), this is just inefficient. There's no reason to take an iterator, build a vector from it, then iterate over the vector. Just use the original iterator.