Generics 不安全队列实现

Generics 不安全队列实现,generics,rust,segmentation-fault,queue,unsafe,Generics,Rust,Segmentation Fault,Queue,Unsafe,我试图创建一个不安全但性能更高的ArrayQueue实现。在我添加测试用例之后,其中一个生成了一个分段错误。 下面是我的简单的最小实现: use std::mem; pub struct ArrayQueue<T> { buff: Vec<T>, head: usize, size: usize, } impl<T> ArrayQueue<T> { pub fn new(size: usize) -> Se

我试图创建一个不安全但性能更高的
ArrayQueue
实现。在我添加测试用例之后,其中一个生成了一个分段错误。 下面是我的简单的最小实现:

use std::mem;

pub struct ArrayQueue<T> {
    buff: Vec<T>,
    head: usize,
    size: usize,
}

impl<T> ArrayQueue<T> {
    pub fn new(size: usize) -> Self {
        let mut buff = Vec::with_capacity(size);

        unsafe {
            buff.set_len(size);
        }

        ArrayQueue {
            buff: buff,
            head: 0,
            size: 0,
        }
    }

    pub fn add(&mut self, elem: T) {
        let idx = (self.head + self.size) % self.buff.len();
        *unsafe { self.buff.get_unchecked_mut(idx) } = elem;
        self.size += 1;
    }

    pub fn remove(&mut self) -> T {
        let idx = self.head;

        self.size -= 1;
        self.head = (self.head + 1) % self.buff.len();
        mem::replace(unsafe { self.buff.get_unchecked_mut(idx) }, unsafe {
            mem::uninitialized()
        })
    }
}

impl<T> Drop for ArrayQueue<T> {
    fn drop(&mut self) {
        let mut idx = self.head;

        for _ in 0..self.size {
            // Drop only valid elements of the queue
            drop(unsafe { self.buff.get_unchecked_mut(idx) });
            idx = (idx + 1) % self.buff.len();
        }

        unsafe {
            // Prevent deallocation of vector elements
            // This still dallocates vector's internal buffer
            self.buff.set_len(0);
        }
    }
}

#[cfg(test)]
mod test {
    use super::ArrayQueue;

    #[test]
    fn test0() {
        let mut x = ArrayQueue::new(10);
        x.add(String::from("K"));
        assert_eq!(x.remove(), String::from("K"));
    }

    #[test]
    fn test1() {
        let mut x: ArrayQueue<Box<String>> = ArrayQueue::new(10);
        x.add(Box::new(String::from("K")));
        assert_eq!(x.remove(), Box::new(String::from("K")));
    }
}
使用std::mem;
发布结构数组队列{
buff:Vec,
负责人:usize,
大小:usize,
}
impl阵列队列{
pub fn new(尺寸:usize)->Self{
让mut buff=Vec::具有_容量(大小);
不安全{
buff.set_len(大小);
}
阵列队列{
牛:牛,
总人数:0,
尺寸:0,
}
}
发布fn添加(&mut self,元素:T){
设idx=(self.head+self.size)%self.buff.len();
*不安全{self.buff.get_unchecked_mut(idx)}=elem;
自身大小+=1;
}
发布fn删除(&mut self)->T{
设idx=self.head;
自我尺寸-=1;
self.head=(self.head+1)%self.buff.len();
mem::replace(不安全{self.buff.get_unchecked_mut(idx)},不安全{
mem::uninitialized()
})
}
}
ArrayQueue的impl Drop{
fn下降(&mut自我){
让mut idx=self.head;
对于0..self.size中的uu{
//仅删除队列的有效元素
drop(不安全{self.buff.get_unchecked_mut(idx)});
idx=(idx+1)%self.buff.len();
}
不安全{
//防止向量元素的释放
//这仍然是向量的内部缓冲区
自抛光设置(0);
}
}
}
#[cfg(测试)]
模试验{
使用super::ArrayQueue;
#[测试]
fn test0(){
让mut x=ArrayQueue::new(10);
x、 添加(字符串::from(“K”);
assert_eq!(x.remove(),String::from(“K”);
}
#[测试]
fn test1(){
让mut x:ArrayQueue=ArrayQueue::new(10);
x、 添加(Box::new(String::from(“K”));
assert_eq!(x.remove(),Box::new(String::from(“K”));
}
}
我相信我正在进行适当的删除,以防止任何内存泄漏

我附加了两个测试用例,其中一个可以工作,但另一个由于无效的内存引用而导致崩溃

它在
add
方法(
*不安全{self.buff.get_unchecked_mut(idx)}=elem;
)中崩溃,我怀疑发生这种情况是因为我试图写入无效的内存位置。
我在测试中专门为向量元素使用了堆分配的对象,但令我惊讶的是,
String
工作正常,而
Box
工作不正常

我想了解是否有可能实现这样一个不安全的实现,以及为什么它目前失败了

编辑


我通过替换
*unsafe{self.buff.get_unchecked_mut(idx)}=elem解决了这个问题使用
不安全的{std::ptr::write(self.buff.get_unchecked_mut(idx),elem)}


现在我想理解为什么当您运行
*unsafe{self.buff.get_unchecked_mut(idx)}=elem时,此功能可以正常工作,而以前的版本不能运行
要替换未初始化的
字符串
,它将在此未初始化的
字符串
上运行
drop
Box
String
都包含一个指针,指向堆中应该存储其数据的部分,当它们被丢弃时,它将在此位置释放内存


通过删除未初始化的
字符串
,它将在任意位置释放内存,因为未初始化的指针可以是任何东西。取消分配尚未分配的内存是未定义的行为。

当您运行
*unsafe{self.buff.get\u unchecked\u mut(idx)}=elem
要替换未初始化的
字符串
,它将在此未初始化的
字符串
上运行
drop
Box
String
都包含一个指针,指向堆中应该存储其数据的部分,当它们被丢弃时,它将在此位置释放内存


通过删除未初始化的
字符串
,它将在任意位置释放内存,因为未初始化的指针可以是任何东西。释放尚未分配的内存是未定义的行为。

我认为这是or的重复。TL;DR-您正在删除尚未初始化的值。您正在将长度设置为一个数字,但该范围内的值是未初始化内存。不,我认为在未初始化内存上调用drop没有任何问题。就我所见,
add
方法在为未初始化的内存赋值时出现了问题,这就是这个不安全代码的问题所在。将新值分配给索引时,正在删除元素。如果您使用(size,T::default)buff.resize_
而不是
set\u len
它将不再崩溃,这纯粹是运气。如上所述,您正在访问未初始化的内存,这是一种未定义的行为。不要这样做;)可以使用
mem::forget
不调用要交换的元素的析构函数。总之,不要这样做。初始化是一次性的,这不是代价高昂的事情。你正在徒劳地破坏生锈的安全目的。这不会发生在
字符串中,因为这是未定义的行为。你无法预测会发生什么或何时发生。它可能会在不同的操作系统、不同的CPU体系结构或未来的Rust编译器中崩溃。我认为这是或的复制品。TL;DR-您正在删除尚未初始化的值。您正在将长度设置为一个数字,但该范围内的值是未初始化内存。不,我认为在未初始化内存上调用drop没有任何问题。就我所见,
add
方法在为未初始化的内存赋值时出现了问题,而这正是pr