One of the core tenets of Rust’s design is making costs explicit, and controllable. The stdlib still needs work on the latter in some areas but overall it’s held quite well.
Something @BurntSushi may have forgotten to mention is that
read_line() actually used to return
io::Result<String>. The current version of the API was decided to be superior for a couple of reasons. I can try to find the RFC for it if my explanation doesn’t convince you.
As previously stated, it allows for the reuse of allocations. While a good system allocator should cache previously freed allocations where possible, Rust allows the expression of this caching in userspace, which helps on platforms that might not have sophisticated allocators.
However, something no one else has said yet, is that taking a reference to an output buffer facilitates much more sane handling of incomplete reads. In the previous design, if an I/O error occurred during reading, all data read up to that point was discarded because
io::Result cannot express an intermediate result. It was suggested that a variant be added that would allow explicit expression of an incomplete result, but it would have been much more of a burden on the user to require them to explicitly handle or ignore an edge case like that.
Also, I don’t think RVO has much of anything to do with reusing heap allocations. IIRC, it changes the return of large structs under the hood so they are written to an out-pointer instead of being copied back to the caller’s stack frame. This is a great optimization nonetheless, but it doesn’t have much bearing on the issue at hand.