5

I recently learned how to read input using io from the Rust documentation, but is there any 'simple' method for reading in console input? My roots are heavily dug into C++, so grabbing input from the console is as easy as std::cin >> var. But in Rust I'm doing:

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

This reads input once, but the for loop seems like a really clumsy way to accomplish this. How can I easily do this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Syntactic Fructose
  • 18,936
  • 23
  • 91
  • 177

3 Answers3

10

The answers above, as @freinn correctly points out, are now out of date. As of Rust 1.0, read_line() requires the caller to pass in a buffer, rather than the function creating & returning one. The following code requires Rust 1.26+ (further simplifying error handling).

Note the response is trimmed using trim_end(). This ensures the newline entered by the user will not be part of the response, which would split the greeting across two lines. Note also the example below is robust in the case the user does not provide a response:

use std::io::stdin;

type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;

fn main() -> Result<()> {
    println!("Hello, there!  What is your name?");

    let mut buffer = String::new();

    // `read_line` returns `Result` of bytes read
    stdin().read_line(&mut buffer)?;
    let res = match buffer.trim_end() {
        "" => "Ah, well I can respect your wish to remain anonymous.".into(),
        name => format!("Hello, {name}.  Nice to meet you!"),
    };
    println!("{res}");

    Ok(())
}
U007D
  • 5,772
  • 2
  • 38
  • 44
  • You can replace that type alias with a `std::io::Result`, as it is the same exact thing. – Maow Jul 05 '21 at 04:52
  • 1
    @Maowcraft Almost, but not exactly the same thing. https://doc.rust-lang.org/src/std/io/error.rs.html#45 shows that specifying the `Error` type is required in `std::io::Result` and the error must be of type `std::io::Error`. Neither of those conditions is true with the above alias. While `std::io::Result` is also a fine choice, the alias above can provide additional flexibility in larger projects, if desired. – U007D Jul 10 '21 at 00:57
9

io::stdin() is in fact a BufferedReader<> wrapping stdin. As you can see in the docs, BufferedReader gives a lot of ways to extract content.

You have notably :

fn read_line(&mut self) -> IoResult<String>

which will try to read a line from stdin (and possibly return an error). A simple code to read an isize from stdin would be :

let a: int = from_str(io::stdin().read_line().unwrap().as_slice()).unwrap()

but it does not any error handling and could easily fail.

A more explicit approach would require you to handle things more cleanly :

let a: isize = match from_str(
    match io::stdin().read_line() {
        Ok(txt) => txt, // Return the slice from_str(...) expects
        Err(e) => { /* there was an I/O Error, details are in e */ }
    }.as_slice() ) /* match from_str(...) */ { 
        Some(i) => i, // return the int to be stored in a
        None => { /* user input could not be parsed as an int */ }
    };
Gurwinder Singh
  • 38,557
  • 6
  • 51
  • 76
Levans
  • 14,196
  • 3
  • 49
  • 53
6

std::io::stdin() returns BufferedReader<StdReader>, and BufferedReader implements Buffer trait. This means that you can call read_line() method on it:

match io::stdin().read_line() {
    Ok(line) => // do whatever you want, line is String
    Err(e) =>   // handle error, e is IoError
}

If you want to read several lines, it is probably better to save io::stdin() result into a variable first.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • 3
    At the present time, the *read_line* function expects one parameter, a mutable reference to a *String*. For more info click [here](https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line). – freinn Feb 27 '17 at 19:17