In the Read
trait, many functions/methods take a buf: &mut XXX
as (one of the) parameter(s) and return Result<usize>
. For example, read_to_string()
takes buf: &mut String
as one of the parameters and returns Result<usize>
.
As I came from many languages, the so-called modern ones usually return the string for a read_string()
function call. The design of the Read
trait in Rust kind of shocks me because it doesn't return the string, but rather passes it as a parameter (similar to C or other more primitive languages).
I'm aware the return value (Result
) is used to indicate whether the read succeeded or not, and it can be passed to match
expressions. (I've learned Lisp and Go previously, so I'm not questioning this kind of design.)
Why didn't the core developers design this trait as "returning the string together with the error message"?
For example, why not design it like this:
fn read(&mut self) -> Result<String> { ... }
The String
also contains the length, so the user can access the length by calling .len()
when needed. The user can concatenate it to any other String
if (s)he wishes, but the key point is that there is no need to create / have one before calling this function.
Is there any particular reasons to design this trait like what it is now?
p.s. I'm still learning Rust (by following The Rust Programming Language), and am reading the second edition after reading most of the first edition. Please correct me if the code above contains any errors (especially when dealing with lifetimes).
I do not know the real reason but there are some advantages:
String
is allocated. You can take it from a pool, reuse it, etc.read_to_string
(or similar) calls. There's no need to concatenate stringsString
can be non-empty), this is why usize
is returned in a Result
The signature
fn read_to_string(&mut self) -> Result<String> { ... }
might look more natural if coming from higher level languages but it gives no control over the String
and its allocation.
RFC 517 (IO reform) states (emphasis mine):
The
read_to_end
andread_to_string
methods now take explicit buffers as input. This has multiple benefits:
Performance. When it is known that reading will involve some large number of bytes, the buffer can be preallocated in advance.
"Atomicity" concerns. For
read_to_end
, it's possible to use this API to retain data collected so far even when a read fails in the middle. Forread_to_string
, this is not the case, because UTF-8 validity cannot be ensured in such cases; but if intermediate results are wanted, one can useread_to_end
and convert to aString
only at the end.
Before Rust 1.0, Reader::read_to_string
did return a String
. It was a deliberate decision to move away from that.
A read_and_create_string
function can be created "on top" of Read::read_to_string
, but the opposite is not true. Code in the standard library needs to be very flexible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With