# vecpool
[](https://crates.io/crates/vecpool)
[](https://docs.rs/vecpool)
[](LICENSE)
A thread-local pool of reusable `Vec` buffers with cross-type reuse.
## Why?
Allocating and deallocating `Vec`s in a loop is expensive. `vecpool` recycles the underlying heap buffers so subsequent allocations are free — just a pointer pop from a thread-local stack.
Unlike per-type pools, `vecpool` keys buffers by *memory layout* `(size, align)`, so a buffer originally used for `Vec<i32>` can be reused for `Vec<u32>`, `Vec<f32>`, or any other 4-byte aligned type.
## Usage
```toml
[dependencies]
vecpool = "0.1"
```
```rust
use vecpool::{get, with_capacity, PoolVec};
// Get a vec from the pool
let mut v = get::<u32>();
v.push(1);
v.push(2);
v.push(3);
// Use it exactly like a Vec<u32>
assert_eq!(v.len(), 3);
assert_eq!(&*v, &[1, 2, 3]);
// On drop, the buffer is returned to the pool
drop(v);
// Next request reuses the buffer — zero allocation cost
let v2 = get::<u32>();
assert!(v2.capacity() >= 3);
```
## Features
- **Cross-type reuse** — `Vec<i32>` ↔ `Vec<u32>` ↔ `Vec<f32>` share the same pool lane
- **Zero synchronization** — one pool per thread, no mutexes or atomics
- **Transparent** — `PoolVec<T>` derefs to `Vec<T>`, all Vec methods work
- **Fast** — power-of-two sizes use a direct array lookup (`trailing_zeros`), no hashing
- **Safe** — all unsafe code is Miri-tested, no undefined behavior
## API Overview
| `get::<T>()` | Get an empty pooled vec |
| `with_capacity::<T>(n)` | Get a pooled vec with at least `n` capacity |
| `PoolVec::new()` | Alias for `get::<T>()` |
| `PoolVec::from(vec)` | Wrap an existing `Vec<T>` for pool return on drop |
| `pool_vec.into_vec()` | Extract the inner `Vec<T>` (skips pool return) |
| `.collect::<PoolVec<T>>()` | Collect an iterator into a pooled vec |
| `pool_vec.into_iter()` | Iterate elements (buffer escapes the pool) |
| `clear_pool()` | Free all pooled buffers on the current thread |
## How It Works
1. Each thread has a local pool with "lanes" keyed by `(size_of::<T>(), align_of::<T>())`
2. Each lane is a LIFO stack of raw buffers (pointer + byte capacity)
3. `get::<T>()` pops a buffer from the matching lane and reconstructs a `Vec<T>` with `len = 0`
4. On drop, `PoolVec<T>` clears elements, extracts the raw buffer, and pushes it back
5. If no buffer is available, a fresh `Vec` is allocated normally
### Fast Path
For types where `size == align` and size is a power of two ≤ 256 (covers all primitives, pointers, and most small types), the pool uses a fixed-size array indexed by `size.trailing_zeros()`. This avoids hashing entirely.
### Thread Safety
`PoolVec<T>: Send` where `T: Send`. If a `PoolVec` is moved to another thread and dropped there, the buffer goes to that thread's pool. No synchronization is needed.
If the thread-local pool is being destroyed (thread exit), the buffer is freed normally instead of being pooled.
## Benchmarks
Run benchmarks with:
```bash
cargo bench
```
The benchmark suite compares pooled vs unpooled allocation across different element sizes, pool depths, and usage patterns.
## Minimum Supported Rust Version
Rust 2024 edition (1.85+).
## License
MIT