Expand description
Thread-local pool of reusable Vec buffers with cross-type reuse.
vecpool eliminates repeated heap allocations by recycling Vec buffers.
When a PoolVec<T> is dropped, its underlying allocation is returned to a
thread-local pool. The next call to get or with_capacity can reuse that
buffer instead of asking the allocator for new memory.
§Key Features
- Cross-type reuse: Buffers are keyed by
(size_of::<T>(), align_of::<T>()), so aVec<i32>buffer can be reused as aVec<u32>(same size and alignment). - Zero synchronization: Each thread has its own pool. No mutexes, no atomics.
- Transparent:
PoolVec<T>dereferences toVec<T>— use allVecmethods directly. - Fast path: Common power-of-two sizes (1–256 bytes) use a direct array lookup instead of hashing.
§Quick Start
use vecpool::{get, with_capacity, PoolVec};
// Get a vec from the pool (or a fresh one if the pool is empty)
let mut v = get::<u32>();
v.push(1);
v.push(2);
v.push(3);
assert_eq!(v.len(), 3);
// On drop, the buffer is returned to the pool
drop(v);
// The next request reuses the same buffer — no allocation!
let v2 = get::<u32>();
assert!(v2.capacity() >= 3);§Cross-Type Reuse
Types with the same memory layout share a pool lane:
use vecpool::get;
let mut v = get::<i32>();
v.push(1);
v.push(2);
let cap = v.capacity();
drop(v);
// u32 has the same size (4) and alignment (4) as i32 — reuses the buffer
let v2 = get::<u32>();
assert_eq!(v2.capacity(), cap);§Pre-allocating Capacity
use vecpool::with_capacity;
// If the pool has a buffer with >= 1000 capacity, reuse it.
// Otherwise, allocate fresh with Vec::with_capacity(1000).
let v = with_capacity::<u64>(1000);
assert!(v.capacity() >= 1000);§Wrapping Existing Vecs
use vecpool::PoolVec;
let data = vec![1u32, 2, 3, 4, 5];
let pooled = PoolVec::from(data);
// When `pooled` is dropped, its buffer enters the pool for future reuse.§Escaping the Pool
use vecpool::get;
let mut v = get::<u32>();
v.push(42);
// Extract the inner Vec — buffer will NOT be returned to the pool
let owned: Vec<u32> = v.into_vec();
assert_eq!(owned, [42]);Calling .into_iter() also escapes the pool (it calls into_vec() internally).
§Collecting Iterators
PoolVec<T> implements FromIterator, so .collect() works directly:
use vecpool::PoolVec;
let v: PoolVec<i32> = (0..100).map(|x| x * 2).collect();
assert_eq!(v.len(), 100);§Memory Management
Pooled buffers persist for the lifetime of the thread. To reclaim memory:
use vecpool::{get, clear_pool};
let mut v = get::<u32>();
v.push(1);
drop(v);
// Free all pooled buffers on this thread
clear_pool();§Thread Safety
PoolVec<T> is Send when T: Send. If moved to another thread and dropped
there, the buffer is returned to that thread’s pool. There is no cross-thread
synchronization.
§Performance
The pool uses a two-tier lookup:
- Fast path: Types with power-of-two size ≤ 256 and natural alignment
(e.g.,
u8,u32,u64,usize) use a direct array index viatrailing_zeros()— a single CPU instruction. - Slow path: All other layouts fall back to an
FxHashMap.
Structs§
Functions§
- clear_
pool - Drain all buffers from the thread-local pool, freeing their memory.
- get
- Get an empty vec from the thread-local pool.
- with_
capacity - Get a vec from the pool with at least
capacityelements of space.