I started programming Rust a couple of days ago by working through the official documentation. Now I'm trying to challenge my understanding of Rust by working through the book "Exercises for Programmers" by Brian P. Hogan (The Pragmatic Programmers).
The first exercise is to write a program that asks the user for a name and prints out a greeting using that name. Input, string concatenation and output should be done in three distinct steps.
What is your name? Patrick
Hello, Patrick, nice to meet you.
The name will be entered at the same line as the initial prompt. Here's my solution:
use std::io;
use std::io::Write;
fn main() {
print!("What is your name? ");
match io::stdout().flush() {
Ok(_) => print!(""),
Err(error) => println!("{}", error),
}
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => {
name = name.trim().to_string();
if name.len() > 0 {
let greeting = "Hello, ".to_string() + &name + &", nice to meet you!".to_string();
println!("{}", greeting);
} else {
println!("No name entered, goodbye.");
}
}
Err(error) => println!("{}", error),
}
}
The print!
macro doesn't actually output the prompt until I call flush
. flush
needs error handling, so I need both to handle the Ok
and the Err
case. In case of Ok
, there's nothing useful to do, so I just print!
an empty string.
Is there a shorter way to handle this? Maybe the error handling can be skipped or simplified somehow, or the whole print!
/flush
approach is the wrong one. (Everything works fine, but I could write this shorter in C, after all...)
As other people have said, make sure to read the error handling chapter.
In most cases, you don't want to use
println!
to report errors. Either you should return the error from your function and let the caller deal with it, or you should usepanic!
to abort that thread and potentially the process.Instead of printing nothing (which is inefficient), you can just... do nothing:
Since you don't care about the success case, you can use an
if let
:Replacing the
println
with apanic!
would be even better:This is almost exactly what
Option::unwrap
does (source), except it also returns the successful value when present:However, it's even better to use
Option::expect
which allows you to specify an additional error message:Applying that twice:
Note that there's no need to re-allocate a
String
, you can just shadowname
, and there's no need to useformat
just to print out stuff.Since Rust 1.26.0, you could also choose to return a
Result
frommain
:I would encourage / challenge you to attempt this. Note that every memory allocation in this program is checked, as is every failure case dealing with the standard output. Many people are not aware that C's
printf
returns an error code that you should be checking. Try outputting to a pipe that has been closed for an example.