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