功能说明
数据库连接、编码缓冲区和解析器初始化成本较高,频繁创建销毁会拖慢服务。对象池预先或按需创建对象,借出后由调用方使用,归还后供下一次复用。Rust 的所有权体系适合用 RAII 表达借还:acquire 返回守卫,守卫实现 Deref 与 DerefMut,使用起来像普通引用;离开作用域时 Drop 自动把对象放回池中,即使发生 panic 也能归还。
本例的池是泛型的,工厂函数负责创建新对象。内部用 Arc 共享池状态,互斥锁保护空闲对象列表,因此可以跨线程克隆使用。main 借用字符串缓冲区写入内容,释放后再次借出同一个对象,验证内容被复用。
关键代码
PoolInner 保存空闲列表和工厂 trait 对象,工厂约束为 Fn 加 Send 加 Sync,保证可跨线程调用。acquire 在锁内弹出对象,池空时在锁内调用工厂;若创建过程较慢,可以改为锁外创建以减少阻塞。PoolGuard 用 Option 保存对象,Drop 时 take 并加锁归还,锁中毒时静默丢弃对象,避免 panic 传播。Deref 与 DerefMut 让守卫直接调用 T 的方法。main 用内层作用域显式触发归还。
使用注意
归还前必须把对象恢复到可用状态,例如清空缓冲区、回滚未提交事务或重置协议状态,否则下一次借用会读到脏数据。池没有容量上限,瞬间大量借用后归还会长期占用内存,应设置最大空闲数并在归还时丢弃多余对象。Drop 中不能执行可能 panic 的操作。对象跨线程使用时 T 必须满足 Send,当前代码未显式加约束,编译器会在实际跨线程时检查。池不负责健康检查,失效连接应在借出时验证。
验证方式
运行脚本,输出 reused=true available=0,说明第一次借用的字符串缓冲区被归还后再次借出,且内容仍然存在。把内层作用域去掉,available 会不同,可观察借用周期对空闲数量的影响。把工厂改为计数闭包,可验证池空时才创建新对象。
代码示例
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()
);
}