tinyvec
A 100% safe Rust crate of vec-like data structures with zero unsafe code, built for no_std and embedded projects.
Repository Health
Technical Analysis
tinyvec provides ArrayVec, SliceVec, and TinyVec — vec-like collection types for Rust that are safe not just at their public API boundary but internally as well, thanks to a crate-wide #![forbid(unsafe_code)]. ArrayVec is a fixed-capacity, array-backed vector that panics on overflow; SliceVec is the same idea backed by a caller-provided &mut [T]; and TinyVec (behind the alloc feature) is an enum that starts Inline in an ArrayVec and automatically promotes itself to Heap (a real Vec) if it outgrows its inline capacity, instead of panicking.
The tradeoff for full memory safety is that element types must implement Default — used to fill freed slots instead of leaving them logically uninitialized. In exchange, the crate sidesteps the class of undefined-behavior bugs that have occasionally surfaced in unsafe-based alternatives like arrayvec and smallvec. The API is deliberately shaped to mirror std::vec::Vec method-for-method, so it drops into existing code with minimal friction, and optional feature flags add serde, borsh, bin-proto, defmt, and schemars support without pulling in dependencies unless asked for.
What You Get
ArrayVec<A>— a fixed-capacity vector backed by a stack array, with aVec-compatible API that panics on overflowSliceVec<'s, T>— the same array-backed behavior but built on a borrowed&mut [T]you supply, useful for reusing existing buffersTinyVec<A>— an inline-or-heap hybrid (behind theallocfeature) that starts on the stack and transparently spills to a heapVecinstead of panicking on overflow- The
array_vec!macro forvec!-style construction ofArrayVecinstances - Optional
serde,borsh,bin-proto,defmt, andschemarstrait implementations gated behind feature flags no_stdcompatibility by default, with an opt-instdfeature for standard-library integration
Common Use Cases
- Avoiding heap allocation for small, bounded collections in performance-sensitive or embedded code
- Building
no_stdfirmware or embedded systems that still want a familiarVec-like API - Replacing
smallvec/arrayvecin codebases that want zerounsafecode for auditability or safety-critical guarantees - Buffering fixed-size batches of data (e.g. network packets, parser tokens) without dynamic allocation
- Serializing/deserializing small inline collections via the optional
serde,borsh, orbin-protointegrations
Under The Hood
Architecture
tinyvec is organized as a small set of focused modules under src/: array.rs defines the Array trait that any fixed-size backing array must implement, arrayvec.rs and arrayvec_drain.rs implement the core ArrayVec type and its draining iterator, slicevec.rs implements the slice-backed variant, and tinyvec.rs (gated behind the alloc feature) implements the Inline/Heap enum that promotes itself automatically on overflow. lib.rs is a thin re-export layer with crate-level docs and feature-gated module wiring — there’s no central runtime or dispatcher, each type is a self-contained struct/enum with inherent methods mirroring std::vec::Vec. The crate-wide #![forbid(unsafe_code)] attribute in lib.rs is the architectural anchor: it structurally prevents any internal unsafe block, meaning the whole design (element defaulting on removal instead of leaving slots uninitialized) is downstream of that one constraint.
Tech Stack
The crate targets Rust 1.47+ on the 2018 edition, is no_std-first with an opt-in std feature, and has no required runtime dependencies — everything beyond the core (tinyvec_macros, serde_core, arbitrary, borsh, generic-array, bin-proto, defmt, schemars) is an optional, feature-gated integration declared in Cargo.toml with default-features = false where applicable, keeping the default build minimal. Development tooling includes criterion for benchmarking (with an optional real_blackbox nightly feature), serde_test and smallvec as dev-dependencies for comparative testing, and a fuzz/ workspace member for fuzz testing. CI runs via GitHub Actions (.github/workflows/rust.yml).
Code Quality
Tests live in tests/arrayvec.rs and tests/tinyvec.rs and are gated behind required-features = ["alloc", "std"] in Cargo.toml, exercising the public API extensively; there’s also a debugger_visualizer test file and a fuzz/ crate for property-style fuzzing. #![warn(missing_docs)] and #![warn(clippy::must_use_candidate)] are enabled crate-wide, and public items carry doc comments with runnable doctests embedded directly in lib.rs and arrayvec.rs. Error handling is minimal by design — capacity violations panic rather than return Result, which is a documented tradeoff rather than an oversight. The #![forbid(unsafe_code)] lint is itself a strong code-quality signal, functioning as a compiler-enforced invariant rather than a convention.
What Makes It Unique
tinyvec’s distinguishing choice is trading the Default bound on element types for a complete absence of unsafe code — where arrayvec and smallvec use unsafe to support any element type and avoid the Default requirement, tinyvec’s TinyVec::Inline-to-Heap promotion and ArrayVec’s free-slot handling both lean on Default::default() to keep every array slot legitimately initialized under Rust’s memory model at all times. This makes the entire crate immune to the class of undefined-behavior bugs that have periodically appeared in unsafe-based alternatives, at the cost of a narrower set of usable element types — a deliberate, clearly-documented tradeoff rather than an accident of implementation.