uninit-read
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 MaybeUninit;
let mut uninit_buf = uninit;
// This is potentially UNDEFINED BEHAVIOR!
let buf_slice = unsafe ;
// 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
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 ;
use UninitRead;
;
// SAFETY: The `read` implementation for `MySafeReader` is well-behaved and
// only writes to the buffer, never reading from uninitialized portions.
unsafe
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 ;
use MaybeUninit;
use ;
// `Cursor<&[u8]>` has `UninitRead` implemented for it (with the `impls` feature).
let mut reader = new;
let mut buffer = ;
// read_uninit abstracts away the unsafe code and returns a slice to the
// initialized part of the buffer.
let initialized_part: & = reader.read_uninit?;
assert_eq!;
Features
This crate uses feature flags to remain lightweight and dependency-free where possible.
-
default:["async", "futures-lite", "impls", "reflection", "exts"] -
exts: (Enabled by default) Provides the [UninitSyncReadExt] and [UninitAsyncReadExt] extension traits for ergonomic reads. -
impls: (Enabled by default) ProvidesUninitReadimplementations for common standard library types likestd::fs::File,std::net::TcpStream, and&[u8]. -
async: Enablesfutures-iosupport. Required for all other async features. -
tokio: ProvidesUninitReadimplementation for TokioCompat. -
reflection: (Enabled by default) Enables theis_uninit_readfunction to check for the trait implementation at runtime.
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:
[]
= { = "0.1", = false }
License
This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.