alloc-checked
Fallible Rust collections that return Result instead of panicking on allocation failure.
Repository Health
Technical Analysis
alloc-checked is a Rust crate maintained by QuestDB that provides drop-in-style wrapper types for the standard library’s collections — currently Vec and VecDeque — built around an explicit Allocator and a Result-first API. Instead of panicking when an allocation fails, every operation that could allocate (push, extend, reserve, resize, with_capacity_in) returns Result<_, TryReserveError>, so allocation failure becomes an ordinary error a caller has to handle rather than an abort.
The crate targets code that genuinely cannot tolerate an unplanned panic from a collection growing under it: embedded and no_std environments, database internals, and other systems software where memory pressure is a real operating condition rather than an edge case. It layers a Claim marker trait (for types that are guaranteed not to panic on clone) and a TryClone trait for fallible cloning on top of the collection types, so higher-level fallible operations compose the same way the standard library’s infallible ones do.
Because it wraps alloc’s existing try_reserve/try_with_capacity_in machinery rather than reimplementing allocation from scratch, the API surface stays close to std::vec::Vec and std::collections::VecDeque, which keeps migration mostly mechanical: swap the import, then work through the compiler errors where a former panic site now needs a ? or explicit match.
What You Get
- A
vec::Vec<T, A>wrapper overalloc::vec::Vecwherepush,extend,reserve,resize,resize_with, andwith_capacity_inall returnResult<_, TryReserveError>instead of panicking - A
vec_deque::VecDeque<T, A>wrapper with the equivalent fallible API surface, built on top of the checkedVec - A
Claimmarker trait identifying types whoseCloneimplementation is guaranteed not to allocate/panic, used to gate fallible-clone-based methods likeextend_from_slice - A
TryClonetrait providing a fallible counterpart toClone, with a defaulttry_clone_fromimplementation - Full
no_stdcompatibility via ano_stdCargo feature, relying only onallocandcore Deref/DerefMutto[T],Index/IndexMut,Debug, and cross-typePartialEqimplementations so the wrapper types behave like slices in most call sites
Common Use Cases
- Embedded firmware or other
no_stdtargets where an allocation failure must be handled as a recoverable error rather than triggering a panic/abort - Database or storage engine internals (the crate originates from QuestDB) that need predictable behavior under memory pressure instead of process-ending panics
- Long-running services where an unbounded or attacker-influenced allocation size should surface as an error path, not a crash
- Migrating existing
std::vec::Vec-based code toward explicit, checked error handling for every growth operation, using the compiler to find every panic-capable call site
Under The Hood
Architecture
The crate is organized around a single wrapper pattern applied twice: vec::Vec<T, A> wraps alloc::vec::Vec<T, A> and reimplements each growth-related method (push, extend, reserve, resize, resize_with, with_capacity_in) as reserve-then-write instead of the standard library’s allocate-or-panic path, using try_reserve under the hood and unsafe pointer writes only after capacity is confirmed. vec_deque::VecDeque<T, A> is built directly on top of the checked Vec rather than reimplementing the same logic against alloc::collections::VecDeque, so the two container types share one source of truth for allocation-fallibility behavior. Two small supporting modules, claim and try_clone, define marker/utility traits (Claim, TryClone) with macro-generated blanket implementations for primitive and standard smart-pointer types, keeping the fallible-clone story orthogonal to the collection implementations themselves. There is no dynamic dispatch or plugin surface — the design is a thin, generic-parameterized shim, so anything that changes the panic-vs-Result contract of the underlying alloc collections would ripple through both wrapper types directly.
Tech Stack
The crate has zero external dependencies — its Cargo.toml declares only the package metadata, a no_std feature flag, and a docs.rs rustdoc configuration, relying entirely on alloc and core from the Rust distribution. It requires the nightly toolchain (pinned via rust-toolchain.toml) because it depends on the unstable allocator_api feature (#![feature(allocator_api)]) to be generic over custom allocators. Build/test tooling is the standard Cargo test harness with no additional build system.
Code Quality
Testing is extensive relative to the crate’s size: vec.rs and vec_deque.rs together carry roughly 50 #[test] functions, exercising push/reserve/extend/truncate/resize/equality/AsRef/AsMut/try_clone paths. A custom testing module implements a WatermarkAllocator that enforces an exact byte budget and asserts allocation/deallocation balance, letting tests assert precisely when an operation should fail with TryReserveError rather than only checking the happy path. A GitHub Actions workflow (.github/workflows/ci.yml) runs against the pinned nightly toolchain. Error handling is explicit throughout — Result<_, TryReserveError> rather than unwrap/expect in the library code itself — and unsafe blocks are narrowly scoped with SAFETY comments justifying each one.
API Design
The core design choice is collapsing the standard library’s split between panicking and try_-prefixed non-panicking APIs (e.g. Vec::with_capacity vs Vec::try_with_capacity_in) into a single, always-fallible surface, so there is exactly one method name to reach for and it always returns Result. This keeps the public API close enough to std::vec::Vec/VecDeque (via Deref<Target = [T]>, Index, symmetric PartialEq) that migrating existing code is largely mechanical, while the Claim/TryClone traits extend the same fallibility guarantee to cloning. The scope is intentionally narrow — only Vec and VecDeque are implemented so far, per the README’s stated ‘per-need basis’ philosophy — which keeps the API surface small and consistent rather than broad.