Rust | How to Read User Input (stdin) In Rust?

It is common to learn how to detect user input (stdin) when learning a new programming language, and Rust is not the exception. In this article, you will learn how to read the user input from a terminal.

Here are the steps to read user input (stdin) in Rust:

  1. Import the std::io crate
  2. Generate a mutable String variable
  3. Generate a variable stdin with an instance of io::stdin()
  4. Trigger the method .read_line() from stdin
  5. Run your code: read the user input

This article will walk you step-by-step to help you understand how to read the user input using the reader io::stdin(). Also, you will learn how to read multiple lines by locking the reader and storing user input data line by line until the user sends no data.

How to read a single line from stdin

For explanation purposes, you will read the user input from themain() function, which is generated by default when creating a new project in the main.rs file.

Import the std::io crate

You will need to create an instance of io::stdin() which is accessible via the std::io. Therefore, import the std::io module in the rust file (.rs) you will read the user input.

use std::io;

Note: There is no need to add std dependency in the dependencies section of the Cargo.toml file. std is Rust Standard Library and is available to all crates by default. std is Rust

Generate a mutable String variable

In the main() function, generate a mutable String variable called user_input. This variable will store at some point the user input.

fn main() -> io::Result<()> {
    let mut user_input = String::new();

    Ok(())
}

Notice the main() function return type definition in the previous snippet of code is io::Result<()> . While that is not necessary, the io::Result type is the type used for functions using the std::io module that can cause errors.

Note: The main() function doesn’t return anything other than () . This is typically used for operations that read user input and log it or print it within the same function. The io:Result<()> type also prevents returning an error in case you don’t want to return errors.

Generate a variable stdin with an instance of io::stdin()

Next, generate an instance io::stdin() and store it in a variable. Storing the instance of io::stdin() in a variable is not required. However, to provide more clarity this tutorial recommends doing so.

let stdin = io::stdin();

What does io::stdin() do?

According to Rust’s documentation, the stdin() function constructs a new handle to the standard input of the current process.

Constructs a new handle to the standard input of the current process.

Each handle returned is a reference to a shared global buffer whose access is synchronized via a mutex.

Rust Function std::io::stdin documentation

What is mutex?

If you read Rust’s documentation definition, you noticed the concept of a mutex.

A mutex is a primitive struct designed to safely mutate data. In other words, mutex allows changing values without the need of using the mut keyword.

Trigger the method .read_line() from stdin

In the next line of code right after generating an instance of io::stdin(), use the stdin.read_line() function to read the user’s input. Pass the mutable reference of the user_input variable.


stdin.read_line(&mut user_input)?;

The read_line() function will read the user’s input in the terminal and store and assign the values to user_input . To verify user_input contains the values the user entered in the terminal, use the println! macro.

println!("input {} ", user_input);

Run your code: read the user input

Feel free to compare your code against the following snippet of code to make sure the logic works as expected.

use std::io;

fn main() -> io::Result<()> {
    let mut user_input = String::new();
    let stdin = io::stdin(); // We get `Stdin` here.
    stdin.read_line(&mut user_input);

    println!("input {} ", user_input);
 
    Ok(())
}

If all is good, open the terminal and run the code using the following command:

cargo run

Note: At the beginning you will think nothing is happening. However, the code is waiting for you to submit your input in the terminal. Feel free to type anything you want and press Enter.

Code login input

If everything is good, you should expect a log from the code with your text you wrote in the terminal. Hence, if you typed Rust is cool!, you should get a log equal to input Rust is cool!

How to read multiple lines from stdin

In the previous example, you learned how to read a single line from stdin. What if you would want to read multiple lines from stdin? The good news is you can achieve this by using io::stdin().lock() function.

Solution

The following snippet of code reads multiple lines from stdin until the user sends an empty input.

use std::io::{self, BufRead};

fn main() -> io::Result<()> {
    let mut lines = io::stdin().lock().lines();
    let mut user_input = String::new();

    while let Some(line) = lines.next() {
        let last_input = line.unwrap();

        // stop reading
        if last_input.len() == 0 {
            break;
        }

        // add a new line once user_input starts storing user input
        if user_input.len() > 0 {
            user_input.push_str("\n");
        }

        // store user input
        user_input.push_str(&last_input);
    }

    println!("\nMulti-line user input \n{}", user_input);

    // the lock is released after it goes out of scope
    Ok(())
}

Code explanation

The mutable lines variable stores an iterator over the lines of an instance of the reader. Notice a couple of things:

  1. Using the lock() function to lock the handle to the standard input (stdin) stream. This means your program will constantly read bytes from, .i.e., the terminal until the lock is released once it goes out of scope.
  2. Importing the trait BufRead or std::io::{self, BufRead}; to access the lines() function. Failing to import the trait BufRead will lead to the following error when compiling the code: error[E0599]: no method named lines found for struct StdinLock in the current scope .
let mut lines = io::stdin().lock().lines();

A mutable user_input variable is created to store input data.

let mut user_input = String::new();

Then, a while is created to iterate over the lines of this reader io::stdin() .

while let Some(line) = lines.next() {
}

Notice how the line variable is defined while determining if the next line (lines.next()) contains a String or an Error.

Inside the loop, the last_input variable stores the string extracted from the line variable using unwrap() .

while let Some(line) = lines.next() {
        let last_input = line.unwrap();

        // stop storing the user input
        if last_input.len() == 0 {
            break;
        }

        // add a new line once user_input starts storing user input
        if user_input.len() > 0 {
            user_input.push_str("\n");
        }

        // store user input
        user_input.push_str(&last_input);
}

Note: You can also unpack the result using the ? operator. Hence, you could replace let last_input = line.unwrap(); for let last_input = line? .

The main logic inside the while loop is to store the user input in user_input variable line by line. Notice, that the code checks whether the user sends data. If the user doesn’t send data and only presses Enter, it will break the loop. Hence, it will stop storing the user input in theuser_input variable.

Finally, log the user_input values to verify the reader read multiple lines from stdin.

println!("\nMulti-line user input \n{}", user_input);

Note: The lock is released after the reader goes out of scope. In the solution presented, this means the reader will stop reading user input once the main function is executed.

Conclusion

In this article, you learned how to read user input in rust by using an instance of io::stdin() and accessing the read_line(&mut user_input) function along with a mutable reference of a String variable to store the user input. Also, you learn how to read multiple lines by locking the reader and detecting and storing each time the user sends an input.

Feel free to check out the code in this article in my repository https://github.com/arealesramirez/rust-read-from-stdin

Did you learn something new?

Share your thoughts about this article on our Twitter account of Become A Better Programmer or on my Twitter account.