# obj-pool
[](https://crates.io/crates/obj-pool)
[](https://docs.rs/obj-pool)
[](https://github.com/artemshein/obj-pool)
A typed object pool with compact 32-bit IDs, optional serde support, and a sharded parallel variant for concurrent workloads.
## Why obj-pool?
When building self-referential data structures (graphs, trees, linked lists) in Rust you have a few options:
1. Unsafe pointer manipulation.
2. `Rc<RefCell<T>>` — safe but verbose and allocation-heavy.
3. A plain `Vec<T>` with index-based access.
`ObjPool<T>` is a polished version of option 3. It handles slot reuse, provides a typed `ObjId` handle instead of a raw `usize`, and catches bugs in debug builds.
## Highlights
- **Compact IDs** — `ObjId` wraps `NonZeroU32`, so `Option<ObjId>` is 4 bytes with no extra space (niche optimization). A `slab` key is a full `usize` (8 bytes on 64-bit).
- **Debug-mode safety** — in debug builds every pool mixes a random pool-specific offset into each `ObjId` it issues, so accidental cross-pool access is rejected (`get` returns `None`, indexing panics) instead of silently returning wrong data. Release builds skip the masking entirely.
- **Parallel pool** — `ParObjPool<T, S>` shards `S` inner pools behind `RwLock`s for concurrent insert/remove/lookup without a global lock.
- **Optional serde** — enable the `serde_support` feature to serialize/deserialize `ObjId`.
## Usage
```toml
[dependencies]
obj-pool = "0.7"
# with serde:
obj-pool = { version = "0.7", features = ["serde_support"] }
```
### Single-threaded pool
```rust
use obj_pool::{ObjPool, ObjId};
let mut pool: ObjPool<String> = ObjPool::new();
let a: ObjId = pool.insert("hello".to_string());
let b: ObjId = pool.insert("world".to_string());
println!("{} {}", pool[a], pool[b]);
pool.remove(a);
// Slot `a` is reused for the next insert.
let c: ObjId = pool.insert("reused".to_string());
```
### Parallel pool
`ParObjPool<T, S>` distributes objects across `S` shards. `ObjId`s are self-contained — the shard index is encoded in the upper bits, so callers need no knowledge of the sharding. The number of bits reserved for the shard index is the smallest able to represent `S` shards, so each shard can hold up to `2^(32 - ceil(log2(S))) - 1` objects (e.g. ~268M per shard for `S = 16`).
```rust
use obj_pool::ParObjPool;
use std::sync::Arc;
let pool: Arc<ParObjPool<u64, 16>> = Arc::new(ParObjPool::new());
let id = pool.insert(42);
// Blocking read — returns a mapped read-guard.
assert_eq!(*pool.get(id).unwrap(), 42);
// Non-blocking — returns None if the shard lock is contended.