Why does rust's read_line function use a mutable reference instead of a return value?

穿精又带淫゛_ 提交于 2021-01-27 12:52:57

问题


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


回答1:


The reason is that read_line is not optimized for 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, in possibly large quantities. When doing that, 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();
}

This ensures that 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. And while it is trivial to implement a more convenient line-reading method in terms of BufRead::read_line, the inverse is not possible.

For example, here is an implementation of an allocating read_line variant that allows the usage as shown above (playground):

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

fn read_new_line(mut input: impl BufRead) -> io::Result<Option<String>> {
    let mut line = String::new();
    if input.read_line(&mut line)? == 0 {
        return Ok(None);
    }
    Ok(Some(line))
}

Note that for use in a while loop something like read_new_line already exists in the form of BufRead::lines(), which returns an iterator over freshly allocated lines. Still, this iterator is designed to be used with iteration so it wouldn't be convenient in the code shown in the question.



来源:https://stackoverflow.com/questions/63627687/why-does-rusts-read-line-function-use-a-mutable-reference-instead-of-a-return-v

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!