#uninitialized-memory #memory-safe #slice #maybe-uninit #buffer #initialization #traits

no-std uninit-tools

A crate that allows working with uninitialized memory entirely in safe code

7 releases

0.0.7 Apr 27, 2021
0.0.6 Apr 11, 2021

#2524 in Rust patterns

MIT license

125KB
2K SLoC

Examples

A Read-like trait implemented to better support uninitialized memory.


use std::mem::MaybeUninit;

use uninit_tools::buffer::{Buffer, BufferRef};
use uninit_tools::traits::Initialize;

pub trait MyRead {
    // NOTE: The function does not return any count, since the buffer keeps track of that.
    //
    // Rather than using `&mut Buffer<T>` directly, we use `BufferRef<'_, T>` to prevent the
    // caller from replacing the buffer that is being filled with something different. It also
    // gives the `Read` implementor a reduced subset of the functionality, to that it cannot
    // for example read the bytes that are already written into the buffer.
    fn read<'buffer, T>(&mut self, buffer: BufferRef<'buffer, T>) -> io::Result<()>
    where
        T: Initialize<Item = u8>,
    ;
}

impl MyRead for &[u8] {
    fn read<'buffer, T>(&mut self, mut buffer: BufferRef<'buffer, T>) -> io::Result<()>
    where
        T: Initialize<Item = u8>,
    {
        // Get the minimum number of bytes to copy. Note that it will panic if the source slice
        // were to overflow, as with the regular `copy_from_slice` function for regular slices.
        let min = std::cmp::min(self.len(), buffer.remaining());

        // Advance the buffer by simply copying the source slice.
        buffer.append(&self[..min]);

        Ok(())
    }
}


// NOTE: The `Initialize` trait is implemented for arrays of all sizes, thanks to const
// generics.
let array = [MaybeUninit::uninit(); 32];
let len = array.len();
let mut buf = Buffer::uninit(array);

let original_stupid_text: &[u8] = b"copying is expensive!";
let mut stupid_text = original_stupid_text;

// Read as many bytes as possible.
stupid_text.read(buf.by_ref())?;

// Note that while we cannot do anything useful with the rest of the buffer, we can still use
// it as the destination of even more I/O, or simply check its length like we do here.
assert_eq!(buf.remaining(), len - original_stupid_text.len());



Note that this may not be the best implementation of the Read trait, but it does show that uninitialized memory handling can be done entirely in safe code, being moderately ergonomic.

(If this would be incorporated into std::io::Read, there would probably be a simpler unsafe function, that defaults to the safer wrapper.)

Dependencies