Rust 如何构建一个可变的vec池,以便在Drop中重用?

Rust 如何构建一个可变的vec池,以便在Drop中重用?,rust,pool,refcell,Rust,Pool,Refcell,我试图创建一个可变的Vec对象池,这些对象可以根据需要传递给函数,并在不再需要时重用(因为我的目标是WASM,我不想让Vec本身释放和重新分配)。我有一个使用Rc和RefCell的实现,我想知道是否有更好(更有效?)的方法来实现这一点 我当前的代码使用Rc::strong_count跟踪我是否已分发缓冲区,并使用RefCell允许可变访问Vec内部: 使用std:{cell::RefCell,rc::rc}; #[导出(调试)] 结构缓冲池{ 缓冲区:Vec, 缓冲区大小:usize, } im

我试图创建一个可变的
Vec
对象池,这些对象可以根据需要传递给函数,并在不再需要时重用(因为我的目标是WASM,我不想让
Vec
本身释放和重新分配)。我有一个使用
Rc
RefCell
的实现,我想知道是否有更好(更有效?)的方法来实现这一点

我当前的代码使用
Rc::strong_count
跟踪我是否已分发缓冲区,并使用
RefCell
允许可变访问
Vec
内部:

使用std:{cell::RefCell,rc::rc};
#[导出(调试)]
结构缓冲池{
缓冲区:Vec,
缓冲区大小:usize,
}
impl缓冲池{
fn new()->Self{
缓冲池{
缓冲区:vec![],
缓冲区大小:3,
}
}
fn添加缓冲区(&mut self)->Rc{
自动缓冲器
.push(Rc::new(RefCell::new(vec![0;self.buffer_size]));
Rc::clone(&self.buffers[self.buffers.len()-1])
}
fn获取缓冲区(&mut self)->Rc{
用于buf in和self.buffers{
//如果Rc计数为1,那么我们还没有将缓冲区借出。
如果Rc::strong_计数(&buf)==1{
返回Rc::clone(&buf);
}
}
//如果我们在这里创建,就没有可用的缓冲区,所以我们需要创建一个。
self.add_buffer()
}
}
此代码可以通过以下方式进行测试:

#[test]
fn test_buffers() {
    let mut buffers = BufferPool::new();
    let buf_cell1 = buffers.get_buffer();
    {
        let mut buf1 = buf_cell1.borrow_mut();
        buf1[0] = 5.5;
    }
    {
        let buf_cell2 = buffers.get_buffer();
        let mut buf2 = buf_cell2.borrow_mut();
        buf2[1] = 6.6;
    }
    {
        let buf_cell3 = buffers.get_buffer();
        let mut buf3 = buf_cell3.borrow_mut();
        buf3[2] = 7.7;
    }
    dbg!(&buffers);
}
其中给出了预期输出:

&buffers=BufferPool{
缓冲区:[
RefCell{
价值:[
5.5,
0.0,
0.0,
],
},
RefCell{
价值:[
0.0,
6.6,
7.7,
],
},
],
缓冲区大小:3,
}
然而,我所做的似乎有点低效,因为
Rc
RefCell::borrow_mut()
都在跟踪缓冲区是否已被“借用”(因为
RefCell
在其内容被双重借用时有出错的能力)。此外,从人体工程学角度看,我不能在一行上调用
缓冲区。get_buffer()。borrow_mut()
,而不抱怨临时值丢失,这让人很恼火


所以,我的问题是:有没有更好的方法来实现这一点?

正如您所注意到的,通过
Rc
提供对物体的访问是可行的,但不符合人体工程学。设计池的一种更好的方法是返回一个获得值所有权的包装器,然后在丢弃时将其放回池中。下面是一个如何做到这一点的“基本”示例:

use std::cell::RefCell;
use std::ops::{Deref, DerefMut};

#[derive(Debug)]
struct BufferPool {
    buffers: RefCell<Vec<Vec<f32>>>,
    buffer_size: usize,
}

impl BufferPool {
    pub fn new() -> Self {
        BufferPool {
            buffers: RefCell::new(Vec::new()),
            buffer_size: 3,
        }
    }
    
    pub fn get_buffer(&self) -> BufferPoolRef {
        let mut buffers = self.buffers.borrow_mut();
        let buffer = buffers.pop().unwrap_or_else(|| vec![0.0; self.buffer_size]);
        BufferPoolRef { pool: self, buffer }
    }
    
    fn return_buffer(&self, buffer: Vec<f32>) {
        let mut buffers = self.buffers.borrow_mut();
        buffers.push(buffer);
    }
}

struct BufferPoolRef<'a> {
    pool: &'a BufferPool,
    buffer: Vec<f32>,
}

impl Deref for BufferPoolRef<'_> {
    type Target = Vec<f32>;
    
    fn deref(&self) -> &Self::Target {
        &self.buffer
    }
}

impl DerefMut for BufferPoolRef<'_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.buffer
    }
}

impl Drop for BufferPoolRef<'_> {
    fn drop(&mut self) {
        let buffer = std::mem::take(&mut self.buffer);
        self.pool.return_buffer(buffer);
    }
}

fn main() {
    let mut buffers = BufferPool::new();
    let mut buf1 = buffers.get_buffer();
    {
        buf1[0] = 5.5;
    }
    {
        let mut buf2 = buffers.get_buffer();
        buf2[1] = 6.6;
    }
    {
        let mut buf3 = buffers.get_buffer();
        buf3[2] = 7.7;
    }
    drop(buf1);
    dbg!(&buffers);
}
然而,你可以用一个类似或的板条箱,而不是自己做这些。它们都在WASM上工作,并使用我上面描述的机制。下面是基于对象池的
BufferPool
实现:

use object_pool::{Pool, Reusable};

struct BufferPool {
    pool: Pool<Vec<f32>>,
    buffer_size: usize,
}

impl BufferPool {
    pub fn new() -> Self {
        BufferPool {
            pool: Pool::new(2, || vec![0.0; 3]),
            buffer_size: 3,
        }
    }
    
    pub fn get_buffer(&self) -> Reusable<Vec<f32>> {
        self.pool.pull(|| vec![0.0; self.buffer_size])
    }
}
使用对象池::{pool,reusabled};
结构缓冲池{
水池:水池,
缓冲区大小:usize,
}
impl缓冲池{
pub fn new()->Self{
缓冲池{
池:池::新建(2,| | vec![0.0;3]),
缓冲区大小:3,
}
}
pub fn get_缓冲区(&self)->可重用{
self.pool.pull(| | vec![0.0;self.buffer_size])
}
}

如果要避免使用
RefCell
,可以使用,但这仅在对
Rc
只有一个引用时有效。我必须有两个参考资料:一个在
BufferPool
中,另一个是借出的……这确实更好!当调整
Vec
的大小时,
pop
是否不会导致内存释放?如果我理解正确,此代码避免使用
Rc
,因为对缓冲区的引用已从
BufferPool
中完全删除,并在drop中重新插入?另一个问题是:在缓冲池示例中,为什么不在
get_buffer(&self)
中使用
mut self
,只有调用
shrink\u以适应
才能做到这一点。“这段代码避免了Rc,因为…”-主要是因为可变引用和共享所有权不能很好地混合。“为什么不需要一个
&mut self
”-它通过
RefCell
使用内部可变性,这是必需的,因为包装器类型仍然保留对池的引用以便将其放回。当这些引用存在时,不能“变异”池。