What is the reason for such an implementation of the io::stdin().read_line(&mut input) method? Why not just return a Result with the appropriate error or input? Why pass &mut input? Does this approach have any advantages?
What is the reason for such an implementation of the io::stdin().read_line(&mut input) method? Why not just return a Result with the appropriate error or input? Why pass &mut input? Does this approach have any advantages?
The huge advantage of this is that because you're passing a mutable reference to an existing String, you can reuse a buffer, or pre-allocate it as needed:
// 2K is a good default buffer size, but definitely do
// analyze the situation and adjust its size accordingly
let mut buffer = String::with_capactity(2048);
// Lock our standard input to eliminate synchronization overhead (unlocks when dropped)
let mut stdin = io::stdin().lock();
// Read our first line.
stdin.read_line(&mut buffer)?;
// This is a stand-in for any function that takes an &str
process_line_1(&buffer);
// Discard the data we've read, but retain the buffer that we have
buffer.clear();
// Reading a second line will reuse the memory allocation:
stdin.read_line(&mut buffer)?;
process_line_2(&buffer)?;
Remember: allocating too much is a lot more efficient than allocating too often. Buffer sharing across different functions may be a little unwieldy due to Rust's borrowing rules (my advice is to have a "cache struct" that keeps empty pre-allocated buffers for a specific function or a collection of APIs), but if you're creating and destroying the buffer within one function, there's minimal work required to get this caching set up and varying potential for performance benefit from caching allocations like this.
What's great about this API is that it not only enables buffer reuse in a clean way, it also encourages it. If you have several .read_line() calls in a row, it immediately feels wrong to create a new buffer for every call. The API design teaches you how to use it efficiently without saying a word. The takeaway is that this tiny trick doesn't just improve performance of I/O code in Rust, it also attempts to guide beginners towards designing their own APIs in this manner, although allocation reuse is sadly often overlooked in third-party APIs. [citation needed]
I believe it has to do with considerations about where the line read will live: on the heap or maybe on the stack of the caller or wherever the caller wants it to be.
Note that a function has no way to return a reference to a value that lives on it's own stack as that value wouldn't live long enough. So the only other option would be to allocate it on the heap or copy the whole thing around, neither of which is desirable from the POV of the caller.
(Please take into account that I am a rust beginner myself, so this answer may be totally wrong. In which case I'm ready to delete it.)