Uninitialized memory comes up a lot when wrapping a C API that has out-parameters. Take for example read. You give it a buffer and a size, and it fills in up to that many bytes of the buffer and tells you how many it actually wrote. Right now the most straightforward way to work with that involves Vec and therefore heap allocation:
unsafe {
let mut buf: Vec<u8> = Vec::with_capacity(1024)
let count = libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.capacity());
if count < 0 {
Err(io::error::last_os_error())
} else {
buf.set_len(count);
buf.shrink_to_fit(); // optional
Ok(buf)
}
}
The stdlib doesn’t actually bother with this; Read::read takes a &mut [u8] from the caller, so you’re working with initialized memory.
Another similar example is pthread_sigmask; here the output buffer is always completely filled in, so you can do
unsafe {
let oldmask: libc::sigset_t = mem::uninitialized();
if libc::pthread_sigmask(how,
&mask as *const libc::sigset_t,
&mut oldmask as *mut libc::sigset_t) {
Err(std::io::last_os_err())
} else {
Ok(oldmask)
}
}
How would you handle these with Uninit<T>? I particularly care about the read case, because I’d really like to be able to write code like this:
let mut buf: Buffer::new(1024); // stack allocation of 1024 uninitialized bytes
while file.read(&mut buf)? > 0 {
// in here `buf` acts like a slice with length
// equal to the value `read` returned
}