Generic RAII Object Pool
Reuse expensive objects through a mutex-backed pool whose guard returns the item automatically on drop, with a factory fallback.
Code
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
struct PoolInner<T> {
items: Mutex<Vec<T>>,
factory: Box<dyn Fn() -> T + Send + Sync>,
}
struct Pool<T> {
inner: Arc<PoolInner<T>>,
}
struct PoolGuard<T> {
inner: Arc<PoolInner<T>>,
item: Option<T>,
}
impl<T> Pool<T> {
fn new<F>(factory: F) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
Self {
inner: Arc::new(PoolInner {
items: Mutex::new(Vec::new()),
factory: Box::new(factory),
}),
}
}
fn acquire(&self) -> PoolGuard<T> {
let item = self
.inner
.items
.lock()
.expect("pool mutex poisoned")
.pop()
.unwrap_or_else(|| (self.inner.factory)());
PoolGuard {
inner: Arc::clone(&self.inner),
item: Some(item),
}
}
fn available(&self) -> usize {
self.inner
.items
.lock()
.expect("pool mutex poisoned")
.len()
}
}
impl<T> Deref for PoolGuard<T> {
type Target = T;
fn deref(&self) -> &T {
self.item.as_ref().expect("guard item is present")
}
}
impl<T> DerefMut for PoolGuard<T> {
fn deref_mut(&mut self) -> &mut T {
self.item.as_mut().expect("guard item is present")
}
}
impl<T> Drop for PoolGuard<T> {
fn drop(&mut self) {
if let Some(item) = self.item.take() {
if let Ok(mut items) = self.inner.items.lock() {
items.push(item);
}
}
}
}
fn main() {
let pool = Pool::new(String::new);
{
let mut first = pool.acquire();
first.push_str("hello");
}
let second = pool.acquire();
println!(
"reused={} available={}",
!second.is_empty(),
pool.available()
);
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.