Expand description
§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.)
Modules§
- buffer
- Buffers for single-buffer and vectored I/O which tracking initializedness and how much has been
filled. Container types pointing to possibly-uninitialized memory such as
Vec<MaybeUninit<Item>>
,IoBox<Uninitialized>
, orBox<[MaybeUninit<Item>]>
can be transformed into their initialized variants via theInitialize
trait, within requiring unsafe code. - buffers
- initializer
- traits
- wrappers
Functions§
- cast_
init_ to_ uninit_ slice - Cast a slice of bytes into a slice of uninitialized bytes, pretending that it is uninitialized.
This is completely safe, since
MaybeUninit
must have the exact same (direct) layout, likeu8
has. The downside with this is that the information about initializedness is lost; unless relying on unsafe code, the resulting slice can only be used to prove validity of the memory range. - cast_
init_ ⚠to_ uninit_ slice_ mut - Cast a mutable slice of bytes into a slice of uninitialized bytes, pretending that it is uninitialized. This is completely safe since they always have the same memory layout; however, the layout of the slices themselves must not be relied upon. The initializedness information is lost as part of this cast, but can be recovered when initializing again or by using unsafe code.
- cast_
uninit_ ⚠to_ init_ slice - Cast a possibly uninitialized slice of bytes, into an initializied slice, assuming that it is initialized.
- cast_
uninit_ ⚠to_ init_ slice_ mut - Cast a mutable slice of possibly initialized bytes into a slice of initialized bytes, assuming it is initialized.
- fill_
uninit_ slice - Fill a possibly uninitialized mutable slice of bytes, with the same
byte
, returning the initialized slice.