The new ReadBuf API is great for safely reading into uninitialized borrowed buffers. But it does not help much with owned buffers, as ReadBuf borrows and there’s no way to convert the buffer into one of the correct type:
fn read<R>(r: &mut R, len: usize) -> Result<Box<[u8]>>
where R: Read
{
let mut buf = Box::new_uninit_slice(len);
let mut read_buf = ReadBuf::uninit(&mut buf);
r.read_buf_exact(&mut read_buf)?;
// Can’t return Box<[u8]> here without unsafe.
}
Luckily, taking a close look at the implementation of Read for Take, it properly supports the ReadBuf API if the underlying Read does. And the default implementation of Read::read_to_end uses the ReadBuf API, abstracting away the unsafe Vec::set_len! So we can implement the above function like follows:
use std::io::{ErrorKind, Read, Result};
fn read<R>(r: &mut R, len: usize) -> Result<Vec<u8>>
where R: Read
{
let mut buf = Vec::with_capacity(len);
let len_u64 = u64::try_from(len).expect("len too big");
let actual = r.take(len_u64).read_to_end(&mut buf)?;
if actual == len {
Ok(buf)
} else {
Err(ErrorKind::UnexpectedEof.into())
}
}
I’ve been using this function and it works well. It can also be adapted to a read_exact_onto<R>(r: &mut R, len: usize, buf: &mut Vec<u8>)
-style API.
But I have two questions:
- Are there other ways to read into an owned buffer without initializing it, without using unsafe?
- What would be the best way to cope with the fallibility of
u64::try_from(len: usize)
?