Reading into uninitialized buffers, yet again

Could this be done by extending the Read trait with a new method like this?

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    // Name subject to bikeshedding. Let's ignore that unimportant detail.
    fn read_uninit<'a>(&mut self, buf: &'a mut [MaybeUninit<u8>]) -> Result<&'a mut [u8]> {
        let ptr = MaybeUninit::first_ptr_mut(buf);
        let init_buf;
        unsafe {
            core::ptr::write_bytes(ptr, 0u8, buf.len());
            init_buf = MaybeUninit::slice_get_mut(buf);
        }
        let len = self.read(init_buf)?;
        return Ok(&mut init_buf[0..len]);
    }
}

The default implementation is suboptimal because it has to initialize the whole buffer, particularly since only a portion of the buffer may be filled. But implementations that explicitly implement read_uninit could be optimal.

I know this is more or less what the Dropbox paper proposes and then discards. I'm not sure I fully understand Dropbox's concerns with the API. I think the read_uninit method could also return Result<(&'a mut [u8], &' mut [MaybeUninit<u8>])>, which is basically buf.split_at_mut(...).

As for the vectored API: I've never used vectored I/O APIs. I'm sure they're useful, but I don't think this should be abandoned just because the vectored API doesn't have an obvious solution. There are plenty of people like me that would benefit from a non-vectored API.

1 Like