compact_str
A memory-efficient Rust string type that inlines up to 24 bytes on the stack, skipping heap allocation for common cases.
Repository Health
Technical Analysis
compact_str provides CompactString, a drop-in replacement for Rust’s String that stores small strings entirely on the stack instead of heap-allocating them. It occupies exactly the same amount of stack space as a String (24 bytes on 64-bit targets), so switching types costs nothing in memory footprint while frequently saving an allocation. Strings longer than that inline limit transparently fall back to heap storage, so the type behaves like String at every size.
The crate is aimed at parsing- and deserialization-heavy code where most strings encountered are short-lived and small — JSON/CSV field values, tokens, identifiers — and the constant overhead of heap allocation dominates. It implements Deref<Target=str>, comparison and conversion traits against String/&str, and ships a format_compact! macro plus ToCompactString/CompactStringExt extension traits, so most call sites can adopt it with minimal rewriting. It supports no_std environments and offers more than twenty optional feature flags integrating with serde, sqlx, diesel, pyo3, rkyv, and other ecosystems.
What You Get
CompactString, asize_of::<String>()-sized type that inlines up to 24 bytes on the stack and heap-allocates only for longer stringsO(1)construction from&'static strviaCompactString::const_new, usable inconstcontexts- The
format_compact!macro, a drop-informat!replacement that produces aCompactStringdirectly ToCompactStringandCompactStringExttraits addingto_compact_string(),join_compact(), andconcat_compact()to existing typesno_stdsupport for embedded and constrained environments- Over twenty optional feature flags wiring
CompactStringinto serde, bytes, sqlx, diesel, pyo3, rkyv, arbitrary/proptest/quickcheck, zeroize, defmt, and more
Common Use Cases
- Parsing and deserialization pipelines that produce large numbers of short-lived, small strings (JSON/CSV fields, tokens, log lines)
- Reducing allocator pressure and improving cache locality in hot paths that build many small strings
- Storing identifiers, keys, or enum-like string values in large in-memory collections where per-element allocation overhead adds up
- Embedded or
no_stdRust code that still needs an owned, growable string type - Interop with existing
String-based codebases viaO(1)/allocation-free conversions fromStringandBox<str>
Under The Hood
Architecture
CompactString wraps a single internal Repr type (src/repr/mod.rs) that is a hand-built tagged union: a discriminant packed into the last byte of the struct (via the LastByte enum) distinguishes an InlineBuffer (a raw stack array), a HeapBuffer (pointer/length/capacity), and a StaticStr variant for strings created from &'static str at zero cost. static_assertions::assert_eq_size! enforces at compile time that every variant is exactly size_of::<String>(), so the layout invariant the whole design depends on is self-checking rather than merely documented. Feature integrations (serde, sqlx, pyo3, rkyv, etc.) live entirely in src/features/*.rs, each gated by its own Cargo feature and implemented purely in terms of the public CompactString API, so the unsafe layout code in repr/ stays isolated from everything that consumes it. src/traits.rs implements ToCompactString/CompactStringExt using the castaway crate for compile-time type specialization on stable Rust, letting the crate special-case integer/float/bool formatting without nightly-only specialization.
Tech Stack
The crate is #![no_std] by default (with an opt-in std feature) and depends on a deliberately small core: castaway for specialization, cfg-if for conditional compilation, itoa for fast integer-to-string formatting, zmij (pinned to an exact version due to a Miri regression), and static_assertions for compile-time layout proofs. Everything else — serde, bytes, diesel, sqlx (mysql/postgres/sqlite), arbitrary/proptest/quickcheck, rkyv, pyo3, bevy_reflect, utoipa, garde, zeroize, defmt, valuable, schemars, borsh, markup, smallvec — sits behind more than twenty additive feature flags. The workspace also carries a bench/ crate built on rayon, a fuzz/ crate, and twelve examples/ crates each demonstrating one optional integration end to end.
Code Quality
The crate carries an extensive test suite (roughly 95 test functions across src/tests.rs) plus feature-gated proptest/quickcheck property tests that check CompactString behavior against alloc::string::String as a reference oracle. A dedicated Kani harness (src/repr/proofs.rs, compiled only under #[cfg(kani)]) formally verifies memory-layout invariants, and CI runs Miri to catch undefined behavior in the crate’s unsafe code. Every unsafe block carries an inline // SAFETY: comment justifying its precondition. GitHub Actions runs separate workflows for MSRV (Rust 1.71), cross-platform targets (32-bit, big-endian), Miri, Kani, clippy, and fuzzing — a CI surface well beyond what’s typical even for widely-used crates. Fallible operations return a typed ReserveError rather than panicking.
API Design
CompactString is designed to be swapped in for String with minimal call-site changes: it implements Deref<Target=str>, equality/ordering against String and &str, and O(1)/allocation-free From<String>/From<Box<str>> conversions that reuse the existing buffer where possible. The format_compact! macro mirrors std::format! exactly, and the ToCompactString/CompactStringExt extension traits add to_compact_string(), join_compact(), and concat_compact() onto existing types without any wrapper boilerplate. Documentation is unusually dense — over a thousand /// doc comments in lib.rs alone, each API surface backed by a runnable doctest — and the crate embeds its own README as the top-level rustdoc via #![doc = include_str!("../README.md")], so crates.io and docs.rs present identical, example-rich documentation. Each of the twenty-plus feature flags ships a runnable example crate under examples/.