🎓 A1Learner
🔍

Rust Reading Standard Input

Standard input is a stream of text piped into a program. Most people call standard input stdin.

Read one line

read_line appends one line to a String you own. It keeps the newline at the end of the line.

let mut first = String::new();
io::stdin().read_line(&mut first)?;
println!("first: {}", first.trim());

Walk every line

Lock stdin once for the whole walk. The lock hands you lines() through the BufRead trait.

for line in io::stdin().lock().lines() {
    let line = line?;
    println!("got: {line}");
}

The filter shape

Read lines. Keep the ones that match. Print those. That small loop is the heart of every grep-like tool.

for line in text.lines() {
    if line.contains(query) {
        println!("{line}");
    }
}

A dash means stdin

Tools use the name "-" to mean stdin. The user picks the source. The rest of the code stays the same.

let path = args.get(2).map(|s| s.as_str()).unwrap_or("-");

Or read it all at once

A small input can arrive in one call. read_to_string fills a String with the whole stream.

let mut text = String::new();
io::stdin().read_to_string(&mut text)?;
println!("chars: {}", text.chars().count());

Example

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

fn main() {
    let stdin = io::stdin();
    let mut n = 0;
    for line in stdin.lock().lines() {
        let line = line.expect("line should read");
        n += 1;
        println!("{n}: {line}");
    }
}

Key points

  • Standard input is the stream a program reads when input is piped into it.
  • Import std::io::BufRead for lines().
  • The trait carries the method, not the stdin type itself.
  • read_line appends to the String.
  • Clear the String before a reuse, or the lines pile up.
  • The end of the input is not an error.
  • lines() simply stops, and read_line returns Ok(0).
  • Lock stdin once for a whole loop.
  • Locking again for every line wastes the buffer each time.

In this example, the run pipes two lines into the program: one and two. stdin.lock() borrows the locked stream once. lines() splits the stream into one String per line. Each line has no trailing newline. The counter n grows by one per line. println! then prints the number, a colon, and the line. The pipe closes after the second line. The loop ends on its own, and the program exits.

Note

Lines from lines() arrive without the trailing newline.

Lines from read_line keep it.

Trim before you compare text.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Which trait must be in scope before stdin.lock().lines() will compile?