I think we can all agree the current Reader and Writer traits are clunky. Not only are they clunky, but have a lot of missed potential. The I/O reform RFC doesn’t really help, and makes some parts even uglier by introducing non-atomic error types everywhere instead of simply removing the offending methods (as they really shouldn’t exist, I’ll go into why later).
Reader also has a lot of overlap with a Iterator<Item = u8>, something which is currently completely ignored aside from a small helper function. Instead, I propose the following:
Amend Iterator with a read() method like so:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
fn read(&mut self, buf: &mut [Self::Item]) -> Option<usize> { ... };
}
Then Reader can simply be an extension trait around Iterator where Item = u8.
Of course what I haven’t mentioned here is the error types. To preserve the functionality of Reader Iterator needs to be able to return an error. But for Iterator to still be able to return an Option the error type would need to be higher kinded. This is where I got stuck.
Iterators being able to return errors is also desirable in various other use cases.
As for why read_to_end & co. shouldn’t exist:
- They’re extremely arbitrary. They will return an error after reading nothing an unspecified number of times, which the documentation nicely specifies as “too many”.
- They’re dangerous.
read_to_end and read_to_string allocate a buffer of 64k, which ties into the above. This makes it easy to unintentionally use copious amounts of memory.
Of course those flaws could be amended, but at that point they’re probably better off reimplemented by the user. With this design read_to_end and read_to_string could easily be replaced with an .extend() call instead (assuming the “no progress” check isn’t needed).