Getting input from the console

Suppose we want to capture the nickname of our player(s) before starting the game, how would we do that? Input or output functionality is handled by the module io in the crate std. It has a function stdin() to read input from the console. This function returns an object of type Stdin, which is a handle to the input stream. The object Stdin has a method read_line(buf) to read a full line of input, that ends with a new line character (that is, when the user hits Enter). This input is read into a String buffer buf. A method is a name for a function defined for a certain type, and it is called using dot notation, like object.method (see Chapter 6, ;Using Traits and OOP in Rust).

So our code will look like this:

let mut buf = String::new(); 
io::stdin().read_line(&mut buf); 

But that is not good enough for Rust, it gives us the following warning:

    warning: unused result which must be used  

Rust is foremost a safe language and we must be ready to cope with everything than can occur. Reading a line might work if an input value is given. But it can also fail, for example, if this code was running in a background job on a machine, so that no console is available to get input from.

How to cope with that? Well, the function read_line() returns a value Result, that can either be a real value (an Ok), when everything works fine, or an error value (an Err), when there is a problem. To cope with a possible error, we need an ok() and an expect() function. The function ok() converts the value Result into a value Option (which contains how many bytes were read), the function expect() gives that value, or shows its message when an error occurs. In Rust a program panics when an error occurs that cannot be recovered from, and the string argument from the function expect() is displayed to tell us where it occurs.

This is written in Rust in a chained form (a bit unusual the first time you see it) as follows:

    io::stdin().read_line(&mut buf).ok().expect("Error!");  

Rust allows us to write these successive calls on separate lines, which for most people clarifies the code a lot:

// from Chapter 4/code/input.rs 
use std::io; 
 
fn main() { 
   println!("What's your name, noble warrior?"); 
   let mut buf = String::new(); 
   io::stdin().read_line(&mut buf) 
              .ok() 
              .expect("Failed to read line"); 
   println!("{}, that's a mighty name indeed!", buf); 
} 

When running this code from the command line we get the following conversation:

    What's your name, noble warrior?
    Riddick
    Riddick
    that's a mighty name indeed!  

Can you guess why that's a mighty name indeed! appears on a new line? Indeed, the input buf still contains a newline character \n! Luckily we have a method trim() to remove trailing and leading whitespace from a string. If we insert the line:

let name = buf.trim(); 
println!("{}, that's a mighty name indeed!", name); 

We now get a correct output, like the following:

    Riddick, that's a mighty name indeed! 

In case the input does not succeed, our program crashes with the following output:

    What's your name, noble warrior?
    thread '<main>' panicked at 'Failed to read line'  

Instead of using this chained .ok().expect() form, you can also use a shorter form with a function unwrap() like this:

io::stdin().read_line(&mut buf).unwrap(); 

This unwraps a value Result, yielding the content of a value Ok (or of a value Some in the case of a value Option), but panics if the value is an Err (or a None for Option), with a panic message provided by the Err's value.

Another alternative is to test with the function is_ok(), which returns true if the result is Ok:

if io::stdin().read_line(&mut buf).is_ok() { 
    let name = buf.trim(); 
    println!("{}, that's a mighty name indeed!", name); 
} 
else { 
    println!("Failed to read line!"); 
} 

How would we read in a positive integer number from the console?

// from Chapter 4/code/pattern_match.rs 
let mut buf = String::new(); 
io::stdin().read_line(&mut buf) 
              .ok() 
              .expect("Failed to read number"); 
   let input_num: Result<u32, _> = buf.trim().parse(); 

We read the number in from the console in a String buffer buf and function trim() the value; the function expect() will show us the message if something goes wrong. But what we have read in this is still a String, we must convert that String to a number.

The method parse() tries to convert the input to an unsigned 32-bit integer in this case. What it returns is in fact again a value Result, this can either be an integer (Ok<u32>), or an error (Err) when the conversion fails.

We will encounter more examples of Option and Result in the section Generics in Chapter 5, Higher Order Functions and Error-Handling.