rust-memory-optimization
Skill1 starsChanged 6 months agoMITView on GitHub
Memory optimization and allocation reduction in Rust. Use when: - User is designing memory-sensitive or low-latency systems - Optimizing data structures to reduce memory footprint - Replacing heap allocations with stack-allocated/bounded structures (SmallVec, ArrayVec, ThinVec) - Handling small strings with optimizations (CompactString) - Reusing collections via clear() in loops - Implementing zero-copy decoding and references - Structuring arena-allocated memory (Bumpalo) - Measuring memory usage, profiling heap, or diagnosing leaks
1 other project uses a version of this.
Adopted by
Most recently taken up by
- tstapler/dotfilestheir own version, first seen Oct 10, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- tstapler/dotfiles★ 8same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository24 other files it gives its agents.
- rust-api-designSkill
- rust-asyncSkill
- rust-axumSkill
- rust-cliSkill
- rust-cloud-nativeSkill
- rust-concurrencySkill
- rust-databaseSkill
- rust-dioxusSkill
- rust-ecosystemSkill
- rust-embeddedSkill
- rust-error-handlingSkill
- rust-fintechSkill
- rust-masterySkill
- rust-mlSkill
- rust-networkingSkill
- rust-observabilitySkill
- rust-ownershipSkill
- rust-design-patternsSkill
- rust-performanceSkill
- rust-securitySkill
- rust-testingSkill
- rust-type-systemSkill
- rust-unsafeSkill
- rust-wasmSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 5 are distinctive, and 10 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 32
ThinVec: Only takes a single pointer size when empty, reducing size in enum layouts. - Very specific: line 58
If a collection's size is determined once at initialization and never changes, convert it from Vec<T> to Box<[T]> to save 8 bytes of stack …
- Very specific: line 68
Instead of cloning or allocating strings, hold references to existing memory using slices or crates like bytes::Bytes.
- Very specific: line 118
Use std::mem::size_of to understand data layout.
- Very specific: line 143
Avoid format! in tight loops.
- Distinctive: line 156
Do not force zero-copy when it creates unmanageable lifetimes for little gain.
- Very specific: line 160
When enums contain Vec variants, ThinVec reduces the enum size by storing only a pointer when empty:
- Very specific: line 213
Prefer Rc when data never crosses thread boundaries.
- Very specific: line 265
Use dhat crate for heap profiling in tests:
- Distinctive: line 285
Keep readability unless the win is meaningful.
- Distinctive: line 330
When reviewing memory optimization, ask what was measured, which allocation is removed, what lifetime assumptions changed, and whether the …
- Very specific: line 336
Prefer borrowing and Cow before cloning strings or buffers.
- Distinctive: line 337
Use arenas only when lifetimes are truly batch-scoped.
- Distinctive: line 338
Check stack growth when replacing heap allocations with inline storage.
- Very specific: line 341
Consider hashbrown or rustc-hash for HashMap-heavy code.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Quick Navigation
- references/stack_allocations.md
- references/arenas.md
Rust Memory Optimization Guide
Advanced strategies for reducing allocations, shrinking layout sizes, and boosting performance.
Core Rules & Patterns
1. Bounded Heap-Free Collections
For collections that are usually small, avoid standard vectors to bypass heap allocations.
SmallVec: Stack-allocated up to a threshold, overflows to the heap.ArrayVec: Fixed-capacity array wrapper; never allocates on the heap.ThinVec: Only takes a single pointer size when empty, reducing size in enum layouts.
use smallvec::{smallvec, SmallVec};
use arrayvec::ArrayVec;
// SmallVec: Allocates 4 items on the stack. Goes to heap only if count > 4.
let mut local_tags: SmallVec<[String; 4]> = smallvec![];
local_tags.push("Rust".to_string());
// ArrayVec: Absolute limit of 8 items on the stack. No heap overflow.
let mut coordinates: ArrayVec<f64, 8> = ArrayVec::new();
coordinates.push(42.0);2. Small String Optimization (SSO)
Standard String consumes 24 bytes and allocates heap space immediately. Use CompactString for inline small strings (under 24 bytes) without allocating.
use compact_str::CompactString;
// No heap allocation for strings <= 24 characters
let username = CompactString::new("user_123");3. Boxed Slices vs Vectors
If a collection's size is determined once at initialization and never changes, convert it from Vec<T> to Box<[T]> to save 8 bytes of stack capacity space.
let items_vec: Vec<i32> = vec![1, 2, 3];
// Save capacity field (shrinks stack representation from 24 to 16 bytes)
let items_slice: Box<[i32]> = items_vec.into_boxed_slice();4. Zero-Copy Patterns
Instead of cloning or allocating strings, hold references to existing memory using slices or crates like bytes::Bytes.
use bytes::Bytes;
pub struct Message {
// Zero-copy reference-counted byte buffer slice
pub payload: Bytes,
}
// Can be sliced and shared among threads without cloning underlying memory
let original = Bytes::from(vec![0; 1024]);
let slice = original.slice(0..100); 5. Arena Allocation
For batch allocation where structures share a lifecycle (e.g. AST parsing), use arena allocators (bumpalo) to allocate items sequentially in a continuous region of memory and free them all at once.
use bumpalo::Bump;
let bump = Bump::new();
// Allocates quickly on the bump arena
let string_ref = bump.alloc_str("dynamic string");
let int_ref = bump.alloc(42);
// Entire arena is cleared at once on drop6. Collection Reuse
Reuse collection allocations in performance-critical loops with .clear() instead of re-instantiating.
// Bad: Allocates a new vector every iteration
for item in feed {
let mut batch = Vec::new();
batch.push(item);
}
// Good: Reuses allocation buffer
let mut batch = Vec::with_capacity(100);
for item in feed {
batch.clear();
batch.push(item);
}Layout Awareness
Use std::mem::size_of to understand data layout.
println!("User size: {}", std::mem::size_of::<User>());Reorder fields to reduce padding only when the type is numerous or hot.
// Often smaller: group large-alignment fields first
struct Record {
id: u64,
flags: u32,
kind: u8,
}String Strategy
fn label<'a>(name: &'a str, fallback: &'a str) -> std::borrow::Cow<'a, str> {
if name.is_empty() { fallback.into() } else { name.into() }
}Avoid format! in tight loops. Reuse buffers with clear().
Zero-Copy Parsing
Return slices into the input when the input outlives parsed data.
pub struct Header<'a> {
pub name: &'a str,
pub value: &'a str,
}Do not force zero-copy when it creates unmanageable lifetimes for little gain.
ThinVec for Enum Payloads
When enums contain Vec variants, ThinVec reduces the enum size by storing only a pointer when empty:
use thin_vec::ThinVec;
// Regular Vec in an enum makes the enum 24+ bytes even when empty
enum Node {
Leaf(i64),
Children(Vec<Node>), // 24 bytes for Vec
}
// ThinVec only stores 8 bytes (a pointer) when empty
enum NodeOptimized {
Leaf(i64),
Children(ThinVec<Node>), // 8 bytes for ThinVec
}Cow for Conditional Ownership
use std::borrow::Cow;
pub struct Error {
message: Cow<'static, str>,
}
impl Error {
// No allocation for static messages
pub fn new(msg: &'static str) -> Self {
Self { message: Cow::Borrowed(msg) }
}
// Allocates only when dynamic
pub fn with_detail(msg: String) -> Self {
Self { message: Cow::Owned(msg) }
}
}Arc vs Rc Trade-offs
use std::rc::Rc;
use std::sync::Arc;
// Single-threaded shared ownership — cheaper than Arc
let shared = Rc::new(large_data);
// Multi-threaded shared ownership — required across threads
let shared = Arc::new(large_data);Prefer Rc when data never crosses thread boundaries. Arc adds atomic reference counting overhead.
HashMap Memory Optimization
use hashbrown::HashMap;
// hashbrown's HashMap uses less memory than std's and is faster
let mut map: HashMap<String, i32> = HashMap::with_capacity(128);
// Reserve capacity upfront to avoid rehashing
map.reserve(expected_count);
// Consider FxHashMap for non-adversarial keys
use rustc_hash::FxHashMap;
let mut fast_map: FxHashMap<String, i32> = FxHashMap::default();Stack vs Heap Decision Matrix
| Type | Stack Size | Heap Alloc | Use When |
String | 24 bytes | Yes | Owns mutable text |
&str | 16 bytes | No | Borrowed text, lifetime bound |
CompactString | 24 bytes | No (≤24 chars) | Short strings that own data |
Cow<str> | 32 bytes | Conditionally | Mostly static, rarely dynamic |
Box<str> | 16 bytes | Yes | Owned fixed string |
Vec<T> | 24 bytes | Yes | Growable collection |
SmallVec<[T; N]> | ~24+ bytes | Only if >N | Usually small, occasionally large |
Box<[T]> | 16 bytes | Yes | Fixed-size after construction |
ThinVec<T> | 8 bytes | Only if non-empty | Empty or small in enums |
Measuring Memory
use std::alloc::{GlobalAlloc, System, Layout};
struct TrackingAllocator;
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
}
// For detailed profiling, use dhat, heaptrack, or valgrindUse dhat crate for heap profiling in tests:
#[cfg(test)]
mod tests {
#[test]
fn memory_test() {
let _profiler = dhat::Profiler::new_heap();
// code under test
// dhat prints allocation stats on drop
}
}Optimization Workflow
- Measure allocations and CPU first.
- Identify the hot path and expected data sizes.
- Pick the narrowest memory change.
- Benchmark before/after.
- Keep readability unless the win is meaningful.
Allocation Decision Rules
Vec<T>: general purpose growable collection.SmallVec<[T; N]>: usually-small collection with occasional heap spill.ArrayVec<T, N>: hard maximum size, no heap allocation.Box<[T]>: fixed-size heap slice after construction.Bytes: shared immutable byte buffers.Bump: many same-lifetime allocations.Cow<'a, T>: borrow unless mutation/ownership is required.
Anti-Patterns
// Bad: cloning a large Vec just to avoid lifetime complexity
let data = original.clone(); // expensive and unnecessary
// Good: use Arc or Cow for shared ownership
let data: Arc<[u8]> = original.into();// Bad: allocating inside a hot loop
for item in items {
let s = format!("processing {}", item); // allocates every iteration
}
// Good: reuse a buffer
let mut buf = String::with_capacity(128);
for item in items {
buf.clear();
write!(buf, "processing {}", item).unwrap();
}- Replacing every
VecwithSmallVecwithout measuring. - Keeping arena references after reset.
- Cloning strings to satisfy ownership instead of shortening borrow scopes.
- Using
Arcas a default clone workaround. - Optimizing layout for types created a handful of times.
- Using
Box<dyn Trait>when generics would eliminate the allocation entirely.
Review Prompt
When reviewing memory optimization, ask what was measured, which allocation is removed, what lifetime assumptions changed, and whether the new type increases stack size or API complexity.
Optimization Checklist
- Measure allocations before changing data structures.
- Preallocate collections only when capacity is known or bounded.
- Prefer borrowing and
Cowbefore cloning strings or buffers. - Use arenas only when lifetimes are truly batch-scoped.
- Check stack growth when replacing heap allocations with inline storage.
- Re-benchmark after changes and keep the simpler version if gains are noise.
- Profile with
dhatorheaptrackto find actual allocation hotspots. - Consider
hashbrownorrustc-hashfor HashMap-heavy code.
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

