uninit-read

Crates.iouninit-read
lib.rsuninit-read
version0.1.1
created_at2025-11-10 16:20:05.095412+00
updated_at2025-11-11 11:57:56.717263+00
descriptionA marker trait and utilities for safe, high-performance reads into uninitialized buffers.
homepage
repositoryhttps://github.com/rrauch/uninit-read
max_upload_size
id1925782
size48,341
Roland Rauch (rrauch)

documentation

https://docs.rs/uninit-read

README

uninit-read

crates.io docs.rs license

A marker trait and utilities for safe, high-performance reads into uninitialized buffers.

The Problem

The standard I/O traits, std::io::Read and futures::io::AsyncRead, require a &mut [u8] buffer. By Rust's safety rules, this slice must be fully initialized. In performance-critical applications, the cost of zeroing a large buffer before a read can be significant.

The common workaround is to create an uninitialized buffer and unsafely cast it to &mut [u8]:

use std::mem::MaybeUninit;

let mut uninit_buf = MaybeUninit::<[u8; 8192] >::uninit();

// This is potentially UNDEFINED BEHAVIOR!
let buf_slice = unsafe { uninit_buf.assume_init_mut() };
// reader.read(buf_slice)?;

This is dangerous because the Read contract does not forbid the implementation from reading from the buffer before writing to it. While most readers don't, the caller is relying on an unverified implementation detail.

A Solution: UninitRead

This crate provides a single, unsafe marker trait that formalizes this contract:

pub unsafe trait UninitRead {}

By implementing UninitRead for a reader type, an author makes a guarantee:

The reader implementation will not read from any part of the provided buffer that has not been written to by the implementation itself within the same call. It must treat the buffer as if it were completely uninitialized on entry.

This contract makes it sound for callers to use an uninitialized buffer with any reader that implements UninitRead.

Usage

For Library Authors (Implementing UninitRead)

If your reader upholds the safety contract, you can implement the trait for it. This is an unsafe impl because you are making a promise the compiler cannot check.

use std::io::{Read, Result};
use uninit_read::UninitRead;

pub struct MySafeReader<R>(R);

impl<R: Read> Read for MySafeReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        // This implementation only writes to `buf` and never reads from it.
        self.0.read(buf)
    }
}

// SAFETY: The `read` implementation for `MySafeReader` is well-behaved and
// only writes to the buffer, never reading from uninitialized portions.
unsafe impl<R: Read> UninitRead for MySafeReader<R> {}

For Crate Users (Consuming UninitRead)

You can use the UninitReadExt traits (enabled by the exts feature) for ergonomic and safe reads into uninitialized buffers.

use std::io::{Cursor, Result};
use std::mem::MaybeUninit;
use uninit_read::{UninitRead, UninitSyncReadExt};

// `Cursor<&[u8]>` has `UninitRead` implemented for it (with the `impls` feature).
let mut reader = Cursor::new([1, 2, 3, 4, 5]);
let mut buffer = [MaybeUninit::<u8>::uninit(); 10];

// read_uninit abstracts away the unsafe code and returns a slice to the
// initialized part of the buffer.
let initialized_part: & [u8] = reader.read_uninit( & mut buffer) ?;

assert_eq!(initialized_part, &[1, 2, 3, 4, 5]);

Features

This crate uses feature flags to remain lightweight and dependency-free where possible.

  • default: ["async", "futures-lite", "impls", "reflection", "exts", "assume-uninit-read"]

  • exts: (Enabled by default) Provides the [UninitSyncReadExt] and [UninitAsyncReadExt] extension traits for ergonomic reads.

  • impls: (Enabled by default) Provides UninitRead implementations for common standard library types like std::fs::File, std::net::TcpStream, and &[u8].

  • async: Enables futures-io support. Required for all other async features.

  • tokio: Provides UninitRead implementation for Tokio Compat.

  • reflection: (Enabled by default) Enables the is_uninit_read function to check for the trait implementation at runtime.

  • assume-uninit-read: Enables the AssumeUninitRead wrapper type, which allows the caller to mark third-party readers as UninitRead-compliant when they are known to uphold the safety contract.

For Library Authors

If you only need to implement the UninitRead trait and do not need any implementations or utilities, you can disable all default features for a dependency-free build:

[dependencies]
uninit-read = { version = "0.1", default-features = false }

License

This project is licensed under either of

at your option.

Commit count: 0

cargo fmt