I agree that we should try to prevent reading from uninitialized memory in safe code, and we should leverage the type system to do so.
I want to point out that &out is not what we want, here (read would be required to fully initialize the buffer before returning). Also, the OpenGL example and the read_to_end example want different things. The same solution will not work for both. read_to_end needs to ensure that a fully safe read doesn’t look at any uninitialized bytes from the buffer (reading bytes that have already been written is theoretically fine, but probably not necessary), writes data into the buffer sequentially, and correctly returns the number of bytes written to the buffer (otherwise uninitialized bytes could end up in the final vector).
It seems like one would want something like this (modulo optimizations):
struct OutBuf<'a> {
buf: &'a mut[u8],
len: usize,
}
impl<'a> OutBuf<'a> {
fn push(&mut self, byte: u8) { self.buf[self.len] = byte; self.len += 1; }
fn push_all(&mut self, slice: &[u8]) { for &byte in slice {self.push(byte);} }
fn capacity(&mut self)->usize { self.buf.len() }
fn len(&mut self)->usize { self.len }
unsafe fn as_mut_ptr(&mut self)->*mut u8 { self.buf.as_mut_ptr() }
unsafe fn set_len(&mut self, len: usize) { self.len = len; }
}
For (my understanding of) the OpenGL example, you want something very different: code can write anywhere in the buffer, it can never read from the buffer, and there’s no need to record how many bytes have been written.