Я пытаюсь создать небезопасную, но более производительную 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")));
}
}
Я полагаю, что делаю правильный сброс, чтобы предотвратить любые утечки памяти.
Я приложил два тестовых случая, где один работает, но другой приводит к сбою из-за неверной ссылки на память.
Сбой внутри метода add
(*unsafe {self.buff.get_unchecked_mut(idx)} = elem;
), и я подозреваю, что это происходит из-за того, что я как-то пытаюсь записать в недопустимую область памяти.
Я специально использовал объекты, выделенные в куче для векторных элементов в тесте, но, к моему удивлению, String
работает правильно, а Box
- нет.
Я хотел бы понять, возможно ли сделать небезопасную реализацию, подобную этой, и почему она в настоящее время дает сбой?
Редактировать
Я исправил проблему, заменив *unsafe {self.buff.get_unchecked_mut(idx)} = elem;
на unsafe {std::ptr::write(self.buff.get_unchecked_mut(idx), elem)};
Теперь я хотел бы понять, почему это работает, а предыдущая версия не