[][src]Crate ioslice

#![no_std]-friendly wrappers over the std::io::IoSlice and std::io::IoSliceMut, which are shared slices and exclusive slices, respectively, and ABI-compatible with system types for I/O vectors.

Internally, the struct will store the following based on crate features:

  • std - wrapping std::io::IoSlice directly, with accessors for it as well as conversion functions and From impls.
  • libc (and #[cfg(unix)]) - wrapping libc::iovec directly on platforms that support it. A marker is also stored, to safely wrap the raw pointer, and forcing usage of this API to follow the borrow checker rules.
  • (none) - wrapping a regular slice, that may not have the same ABI guarantees as the types from std or libc have.

IoSlice will however implement AsRef<[u8]>, Borrow<[u8]>, and Deref<Target = [u8]> regardless of the features used.


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

use std::mem::MaybeUninit;

use ioslice::{Initialization, Initialized, IoSliceMut, InitializePartialExt};

pub trait MyRead {
    // NOTE: This could be a regular slice as well.
    fn read<'a, I: Initialization>(&mut self, slice: IoSliceMut<'a, I>) ->
    io::Result<(IoSliceMut<'a, Initialized>, IoSliceMut<'a, I>)>;

impl MyRead for &[u8] {
    fn read<'a, I: Initialization>(&mut self, slice: IoSliceMut<'a, I>) ->
    io::Result<(IoSliceMut<'a, Initialized>, IoSliceMut<'a, I>)> {
        // Begin with taking the minimum slice that can fit the copy into the buffer.
        let bytes_to_copy = std::cmp::min(self.len(), slice.len());
        let (source, source_remainder) = self.split_at(bytes_to_copy);

        // Split the possibly uninitialized slice into an initialized and an uninitialized
        // part. This allows the `Read` implementation to only read part of the data requested.
        // Normally this would be done via returning `usize`, but we need to be able to prove
        // that we have initialized it.
        let (initialized, remainder) = slice.partially_init_by_copying(source);

        // Advance the slice that is being read.
        let bytes_copied = initialized.len();
        *self = source_remainder;

        // And finally, return the initialized part and the remainder to prove that we have
        // initialized it. (And to make it possible for safe code to use the buffer directly.)
        Ok((initialized, remainder))

let mut buf = [MaybeUninit::uninit(); 32];
let buf = IoSliceMut::from_uninit(&mut buf);
let len = buf.len();

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

// Read as many bytes as possible.
let (initialized, remainder) = stupid_text.read(buf)?;
assert_eq!(initialized, original_stupid_text);

// Note that while we cannot read the rest of the buffer, we can still use it as the
// destination of even more I/O, or simply check its length here.
assert_eq!(remainder.len(), 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.)





An error that may occur if allocating an I/O box fails.


An owned chunk of memory, that is ABI-compatible with libc::iovec or WSABUF, depending on the platform and Cargo features used.


A #![no_std]-friendly wrapper over the std::io::IoSliceMut.


A #![no_std]-friendly wrapper over the std::io::IoSliceMut.





A trait for mutable slices (there is not much to do at all with an uninitialized immutable slice, is there?), that can be safely casted to an uninitialized slice, but also unsafely assumed to be initialized when they may not be.


A trait for slices (or owned memory) that contain possibly uninitialized slices themselves.


A trait for mutable slices, that can also be split. This allows partially initializing a slice and return both the initialized and the uninitialized part, completely in safe code.




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, like u8 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 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 a possibly uninitialized slice of bytes, into an initializied slice, assuming that it is initialized.


Cast a mutable slice of possibly initialized bytes into a slice of initialized bytes, assuming it is initialized.