How can I read user input in Rust?

InputIoRust

Input Problem Overview


I intend to make a tokenizer. I need to read every line the user types and stop reading once the user presses Ctrl + D.

I searched around and only found one example on Rust IO which does not even compile. I looked at the io module's documentation and found that the read_line() function is part of the ReaderUtil interface, but stdin() returns a Reader instead.

The code that I would like would essentially look like the following in C++:

vector<string> readLines () {
    vector<string> allLines;
    string line;

    while (cin >> line) {
        allLines.push_back(line);
    }

    return allLines;
}

> This question refers to parts of Rust that predate Rust 1.0, but the general concept is still valid in Rust 1.0.

Input Solutions


Solution 1 - Input

Rust 1.x (see documentation):

use std::io;
use std::io::prelude::*;

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        println!("{}", line.unwrap());
    }
}

Rust 0.10–0.12 (see documentation):

use std::io;

fn main() {
    for line in io::stdin().lines() {
        print!("{}", line.unwrap());
    }
}

Rust 0.9 (see 0.9 documentation):

use std::io;
use std::io::buffered::BufferedReader;

fn main() {
    let mut reader = BufferedReader::new(io::stdin());
    for line in reader.lines() {
        print(line);
    }
}

Rust 0.8:

use std::io;

fn main() {
    let lines = io::stdin().read_lines();
    for line in lines.iter() {
        println(*line);
    }
}

Rust 0.7:

use std::io;

fn main() {
    let lines = io::stdin().read_lines();
    for lines.iter().advance |line| {
        println(*line);
    }
}

Solution 2 - Input

As of 17 April 2015, from mdcox on the Firefox Rust IRC channel.

use std::io;

fn main() {
    let mut stdin = io::stdin();
    let input = &mut String::new();

    loop {
        input.clear();
        stdin.read_line(input);
        println!("{}", input);
    }
}

Solution 3 - Input

In Rust 1.0 and later, you can use the lines method on anything that implements the std::io::BufRead trait to obtain an iterator over lines in the input. You could also use read_line , but using the iterator is more likely what you'd want. Here is a version of the function in the question using iterators; see below for a more detailed explanation. (playground link)

use std::io;
use std::io::prelude::*;

pub fn read_lines() -> Vec<String> {
    let stdin = io::stdin();
    let stdin_lock = stdin.lock();
    let vec = stdin_lock.lines().filter_map(|l| l.ok()).collect();
    
    vec
}

And here's a version that is more like the C++ version in the question, but is not really the idiomatic way to do this in Rust (playground):

use std::io;
use std::io::prelude::*;

pub fn read_lines() -> Vec<String> {
    let mut vec = Vec::new();
    let mut string = String::new();
    
    let stdin = io::stdin();
    let mut stdin_lock = stdin.lock();
    
    while let Ok(len) = stdin_lock.read_line(&mut string) {
        if len > 0 {
           vec.push(string);
           string = String::new();
        } else {
            break
        }
    }

    vec
}

To obtain something that implements BufRead, which is needed to call lines() or read_line(), you call std::io::stdin() to obtain a handle to standard input, and then call lock() on the result of that to obtain exclusive control of the standard input stream (you must have exclusive control to obtain a BufRead, because otherwise the buffering could produce arbitrary results if two threads were reading from stdin at once).

To collect the result into a Vec<String>, you can use the collect method on an iterator. lines() returns an iterator over Result<String>, so we need to handle error cases in which a line could not be read; for this example, we just ignore errors with a filter_map that just skips any errors.

The C++ like version uses read_line, which appends the read line to a given string, and we then push the string into our Vec. Because we transfer ownership of the string to the Vec when we do that, and because read_line would otherwise keep appending to the string, we need to allocate a new string for each loop (this appears to be a bug in the original C++ version in the question, in which the same string is shared and so will keep accumulating every line). We use while let to continue to read until we hit an error, and we break if we ever read zero bytes which indicates the end of the input.

Solution 4 - Input

The question is to read lines from standard input and return a vector. In Rust 1.7:

fn readlines() -> Vec<String> {
    use std::io::prelude::*;
    let stdin = std::io::stdin();
    let v = stdin.lock().lines().map(|x| x.unwrap()).collect();
    v
}

Solution 5 - Input

There are few ways I can think of.

Read all the input into single String

let mut input = String::new();
io::stdin().read_to_end(&mut input);

Read lines into Vector. This one doesn't panic when reading a line fails. Instead it skips that failed line.

let stdin = io::stdin();
let locked = stdin.lock();
let v: Vec<String> = locked.lines().filter_map(|line| line.ok()).collect();

Furthermore if you want to parse it:

After reading it into string do this. You can parse it to other collections that implements FromIterator. Contained elements in the collection also must implement FromStr. As long as the trait constraint satisfies, you can change Vec to any Collection:FromIterator, Collection<T: FromStr>:

let v: Vec<i32> = "4 -42 232".split_whitespace().filter_map(|w| w.parse().ok()).collect();

Also, you can use it on the StdinLock:

let vv: Vec<Vec<i32>> = locked
    .lines()
    .filter_map(|l|
        l.ok().map(|s|
            s.split_whitespace().filter_map(|word| word.parse().ok()).collect()
        )
    )
    .collect();

Solution 6 - Input

Uh... After many trials and errors, I've found a solution.

I'd still like to see a better solution so I'm not going to accept my own solution.

The code below prints exactly what the user inputs.

mod tokenizer {

    pub fn read () -> ~[int] {
        let reader = io::stdin();
        let mut bytes: ~[int] = ~[];

        loop {
            let byte: int = reader.read_byte();
            if byte < 0 {
                return bytes;
            }
            bytes += [byte];
        }
    }
}

fn main () {
    let bytes: ~[int] = tokenizer::read();
    for bytes.each |byte| {
        io::print(#fmt("%c", *byte as char));
    }
}

> This answer predates Rust 1.0. Please see the other answers for modern solutions.

Solution 7 - Input

In Rust 0.4, use the ReaderUtil trait to access the read_line function. Note that you need to explicitly cast the value to a trait type, for example, reader as io::ReaderUtil:

fn main() {
    let mut allLines = ~[];
    let reader = io::stdin();

    while !reader.eof() {
            allLines.push((reader as io::ReaderUtil).read_line());
    }

    for allLines.each |line| {
            io::println(fmt!("%s", *line));
    }
}

> This answer predates Rust 1.0. Please see the other answers for modern solutions.

Solution 8 - Input

From Processing a Guess

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess).expect("Failed to read line");

    println!("You guessed: {}", guess);
}

While there are many different ways of doing this in Rust, I think this is the simplest to implement.

use std::io;
// ...
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read line");

    println!("Your input was `{}`", input);
// ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJimmy LuView Question on Stackoverflow
Solution 1 - InputrobinstView Answer on Stackoverflow
Solution 2 - InputgaigeprView Answer on Stackoverflow
Solution 3 - InputBrian CampbellView Answer on Stackoverflow
Solution 4 - InputAethanycView Answer on Stackoverflow
Solution 5 - InputDulguun OtgonView Answer on Stackoverflow
Solution 6 - InputJimmy LuView Answer on Stackoverflow
Solution 7 - InputBilal HusainView Answer on Stackoverflow
Solution 8 - Input9716278View Answer on Stackoverflow