heapless

Static, no_std data structures like Vec, String, and Deque with fixed capacity — no heap allocator required.

Library
Cargo
v0.9.3
2,013stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
84/100Excellent
Development Activity88
Maintenance72
Community76
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
82/100Excellent
Architecture85
Code Quality90
Innovation78
Learning Curve75

heapless is a Rust crate providing static-friendly data structures — Vec, String, Deque, BinaryHeap, IndexMap, IndexSet, LinearMap, HistoryBuf, and more — that store their memory inline via a compile-time capacity type parameter instead of relying on a heap allocator. Because capacity is fixed, operations like push are truly constant-time rather than amortized, and there’s no risk of an uncatchable out-of-memory condition: capacity-exhausting operations return a Result instead of panicking or silently reallocating.

This makes heapless a foundational building block in the embedded Rust ecosystem, widely used in no_std firmware, RTOS tasks, and other memory-constrained or hard-real-time contexts where a global allocator is unavailable or undesirable. It also ships lock-free spsc/mpmc queues and a pool module for allocator-free object pooling on platforms with suitable atomic support, plus optional integrations with serde, defmt, ufmt, zeroize, and embedded-io.

What You Get

  • Vec<T, N> and String<N> - fixed-capacity drop-in equivalents to std::Vec/String with capacity fixed at compile time via the const generic N.
  • Deque, BinaryHeap, HistoryBuf, LinearMap, IndexMap/IndexSet - additional static-capacity collections covering queues, priority queues, ring-buffer history, and small hash-based maps/sets.
  • Lock-free spsc and mpmc queues - single-producer/single-consumer and multi-producer/multi-consumer queues usable from interrupt handlers without a mutex.
  • Object pool (pool module) - lock-free memory pool backing Arc/Box/Object types as an allocator-free alternative to Rust’s global allocator, on targets with suitable atomics.
  • Optional trait integrations - serde, defmt::Format, ufmt, zeroize::Zeroize, and embedded-io::Write support behind Cargo feature flags.

Common Use Cases

  • Firmware without a heap - building no_std embedded firmware that needs Vec/String-like ergonomics without pulling in a global allocator.
  • Hard real-time systems - RTOS tasks or interrupt handlers that require truly constant-time push/pop instead of amortized reallocation.
  • Interrupt-safe producer/consumer pipelines - passing data between an ISR and main-loop code via heapless::spsc::Queue without locks.
  • Bounded buffering in resource-constrained services - any Rust service, embedded or not, that wants a hard cap on memory usage for a buffer or cache instead of unbounded growth.

Under The Hood

Architecture The crate is organized as one module per data structure (vec, string, deque, binary_heap, history_buf, index_map, index_set, linear_map, mpmc, spsc, pool, sorted_linked_list), each built on a shared Storage/VecStorage-style sealed-trait abstraction defined in src/storage.rs and mirrored per-container (e.g. src/vec/mod.rs’s VecStorage/VecSealedStorage). That trait lets a single VecInner<T, LenT, S> generic type back both the owned, const-generic-sized Vec<T, N> and an unsized VecView<T>, so a Vec can be unsized into a VecView via coercion or .as_view() — the same pattern repeats for Deque and BinaryHeap, which reuse VecStorage internally. This design means core abstractions like VecStorage ripple through nearly every container if changed, since binary_heap and deque are implemented in terms of the vec module’s storage trait rather than independently.

Tech Stack Pure Rust, #![no_std] by default (only std for its own test harness), edition 2021, MSRV-unpinned per policy. Core dependency is hash32 (32-bit-friendly hashing for IndexMap/IndexSet); everything else — bytes, portable-atomic, serde_core, ufmt/ufmt-write, defmt, zeroize, embedded-io, stable_deref_trait — is gated behind Cargo features and pulled in only when needed, keeping the default build dependency-free. build.rs probes target atomic support to enable mpmc/spsc/pool conditionally based on the compilation target’s atomic width.

Code Quality Testing is unusually rigorous for a crate this size: in addition to unit tests embedded per-module, the CI (.github/workflows/build.yml) runs the full test suite under MIRI (cargo miri test) across every feature combination to catch undefined behavior, plus a cfail/ directory of trybuild-style UI tests asserting specific code fails to compile (e.g. capacity misuse). #![deny(missing_docs)] and a wide #![warn(clippy::...)] lint set (use_self, ptr_as_ptr, doc_markdown, etc.) are enforced crate-wide, and CapacityError is returned as a typed Result rather than panicking on overflow. Combined with a 126-contributor history and steady CHANGELOG discipline, this reflects a mature, safety-critical-conscious codebase.

What Makes It Unique Unlike general-purpose fixed-size-buffer crates, heapless commits to array-backed collections that are literally interchangeable with core::sync::atomic-based lock-free queues and a Treiber-stack-based lock-free object pool (src/pool/treiber.rs) in the same crate — giving embedded developers both static collections and a lock-free allocator alternative from one dependency instead of stitching together separate crates for buffers, queues, and pooling.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search