问题
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