8

Consider this code to read the user input in rust

use std::io;
fn main() {
    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .expect("error: unable to read user input");
    println!("{}", input);
}

why is there no way to do it like this?

use std::io;
fn main() {
    let mut input = io::stdin()
        .read_line()
        .expect("error: unable to read user input");
    println!("{}", input);
}

it would be more convenient to other languages

Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
Kingsly2407
  • 476
  • 4
  • 8
  • If you need, you can easly write a function that does what you want. Moreover, you can do it by using `read_line`, but you can't make the other way around. – Ekrem Dinçel Dec 31 '20 at 10:44

1 Answers1

15

TL;DR The closest you have is lines(), and the reason read_line works like it does is efficiency. The version that uses lines() looks like this:

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

fn main() {
    // get next line from stdin and print it
    let input = io::stdin().lock().lines().next().unwrap().expect("IO error");
    println!("{}", input);
}

In general, read_line() is not designed for use in small interactive programs; there are better ways to implement those.

The read_line method comes from the generic io::BufRead trait, and its primary use is reading input, typically redirected from files or other programs, and possibly coming in large quantities. When processing large amounts of data, it is advantageous to minimize the number of allocations performed, which is why read_line is designed to reuse an existing string. A typical pattern would be:

let mut line = String::new();
while input.read_line(&mut line)? != 0 {
    // do something with line
    ...
    line.clear();
}

The number of (re-)allocations is kept minimal, as line will grow only as needed to accommodate the input lines. Once a typical size is reached, allocations will become very rare, and once the largest line is read, they will disappear altogether. If read_line() supported the "convenient" interface, then the above loop would indeed look nicer - for example:

while let Some(line) = read_new_line(some_input)? {
    // process the line
    ...
}

...but would require a new allocation and deallocation for each line. In throw-away or learning programs this can be perfectly fine, but BufRead is intended as a building block for efficient IO, so its read_line method favors performance over convenience.

user4815162342
  • 141,790
  • 18
  • 296
  • 355