Hello all, I am very new to Rust. I am currently building a command line program, and the only way for me to capture typed user sentences i.e., "the quick brown fox", as a vector and check for specific words in specific indexes. For instance, I want to make sure the first word is taken into consideration and something happens. Below is the code I am currently using, and its the only way I was able to get the code to match and do specific actions based upon my criteria. Please review and tell me if this is the proper way to format it, as it seems off to me. Appreciate any and all suggestions.
use std::io::stdout;
use std::io::stdin;
use std::io::Write;
fn main() {
loop {
print!("[Rust] >> ");
stdout().flush();
let mut input = String::new();
stdin().read_line(&mut input).unwrap();
input.pop();
let mut v: Vec<String> = Vec::new();
let command = input.split(" ");
for x in command {
v.push(x.to_string());
}
if v.len() > 3 {
println!("Too many arguments.");
} else {
// currently this else part is just a placeholder.
println!("{:?}", v);
}
// Here comes the part I think is not the best syntax??
match v {
_ if &v[0].to_lowercase() == "1" => println!("You typed in the number 1."),
_ if &v[0].to_lowercase() == "2" => println!("You typed in the number 2."),
_ if &v[0].to_lowercase() == "3" => println!("You typed in the number 3."),
_ if &v[0].to_lowercase() == "4" => println!("You typed in the number 4."),
_ if &v[0].to_lowercase() == "5" => println!("You typed in the number 5."),
_ if &v[0].to_lowercase() == "6" => println!("You typed in the number 6."),
_ if &v[0].to_lowercase() == "cheese" => println!("Your first word was 'cheese'."),
_ if &v[0].to_lowercase() == "plate" => println!("Your first word is 'plate'."),
_ => println!("Didn't catch it."),
};
}
To print Didn't catch it. this code allocates 8 temporary Strings of same text which is not super efficient. But the real problem is it's too verbose. You can at least write it like this:
match &v[0].to_lowercase()[..] {
"1" => println!("You typed in the number 1."),
"2" => println!("You typed in the number 2."),
"3" => println!("You typed in the number 3."),
"4" => println!("You typed in the number 4."),
"5" => println!("You typed in the number 5."),
"6" => println!("You typed in the number 6."),
"cheese" => println!("Your first word was 'cheese'."),
"plate" => println!("Your first word is 'plate'."),
_ => println!("Didn't catch it."),
}
You might be interested in a feature called "slice patterns". A while back, I wrote up an article walking through how it works and one of the examples does almost exactly what you are trying to do.
You do something like this to create a Vec<&str>where each &str points to a word in the original input:
fn main() {
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
let words: Vec<&str> = input.split_whitespace().collect();
let args = parse_args(&words);
println!("{args:?}");
}
As well as being simpler than the version with manual pop()s and push()es, it's also more efficient because we only ever create one owned string (input) and our words just reference parts of that string.
Thank you for this comment Michael. I will be looking into this, as it seems this is way better suited for my particular use case. Want the code to work, but also be efficient. So thank you for providing this information.
It's worth mentioning that it probably takes longer for the printed output to be sent to your terminal's process and rendered to the screen than it does to run that whole example to finish.
So while it's nice to avoid unnecessary string copies when you can (like we did there), there's no need to bend over backwards to avoid them... Your bottlenecks are almost always elsewhere, and modern computers are fast!