Greetings, please note this is me learning, i know how ppl feel about unsafe code in rust, but i need to learn it than just sweep it under the rug.
Question,
I decided to implement Queue Data structure and using unsafe. the data structure seems to be working fine and all my test passed. but including it in another project i was working on, i keep getting error
Here is my Queue DS
// Queue Data Structure implementation. NOTE:: For learning purposes only,
// would not recommend using it in anything other than testing it
use std::ptr;
use std::mem;
use tracing::info;
pub type Result<T> = std::result::Result<T, QueueError>;
pub trait QueueInterface {
type Item;
/// add item unto the queue
fn enqueue(&mut self, item: Self::Item) -> Result<()>;
/// remove item unto the queue
fn dequeue(&mut self) -> Option<Self::Item>;
/// returns the size of the queue
fn size(&self) -> isize;
/// return `true` is queue is empty otherwise return `false`
fn is_empty(&self) -> bool;
/// return `true` is queue is empty otherwise return `false`
fn is_full(&self) -> bool;
}
#[derive(Clone, Copy)]
pub struct Queue<T: Sized + Clone> {
buf: QueueBuf<T>,
head: *mut T,
tail: *mut T,
}
unsafe impl<T> Send for Queue<T> where T: Clone {}
impl<T> Queue<T> where T: Sized + Clone {
/// create an instance of Queue with a default capacity
///
/// default capaity is set to 100
pub fn new(capacity: usize) -> Queue<T> {
let mut buf = Vec::<T>::with_capacity(capacity);
let buf_ptr = buf.as_mut_ptr();
//let raw_f2 = ptr::addr_of!(buf_ptr);
let ff = format!("{:?}", buf_ptr);
tracing::info!(address_of_buf_ptr = ff.as_str());
let buffer: QueueBuf<T>;
buffer = unsafe {
QueueBuf {
start: buf_ptr,
end: buf_ptr.offset((buf.capacity() - 1).try_into().unwrap()),
}
};
// prevent rust from dropping the vec memory
// intentionally leaking memory
mem::forget(buf);
Queue {
buf: buffer,
head: ptr::null_mut(),
tail: ptr::null_mut(),
}
}
}
impl<T> QueueInterface for Queue<T> where T: Sized + Clone {
type Item = T;
/// returns a new Queue object large enough to of
/// data of size: size.
fn enqueue(&mut self, item: Self::Item) -> Result<()> {
//let tail = ptr::addr_of!(self.tail);
//let head = ptr::addr_of!(self.head);
//let ph = format!("{:?}", head);
//let pt = format!("{:?}", tail);
//tracing::info!(address_of_buf_ptr = ff.as_str());
if self.is_full() {
return Err(QueueError::Full);
}
// check if queue is empty, if queue is empty return with an error
// an ideal situation would be to just return None
if self.is_empty() {
self.head = self.buf.start as *mut T;
self.tail = self.buf.start as *mut T;
let ph = format!("{:?}", self.head);
let pt = format!("{:?}", self.tail);
unsafe {
tracing::info!(address_of_buf_ptr = ph.as_str());
tracing::info!(address_of_buf_ptr = pt.as_str());
*(self.tail) = item; // <= this is where it panics, but works fine during testing
tracing::info!("Let see");
}
return Ok(());
}
self.tail = unsafe { self.tail.offset(1) };
unsafe {
*(self.tail) = item;
}
return Ok(());
}
fn size(&self) -> isize {
unsafe {
// adding one to the offset gives the actual size of the data
// on the buffer
self.tail.offset_from(self.head) + 1 as isize
}
}
fn dequeue(&mut self) -> Option<Self::Item> {
let item: Self::Item;
// first check if it is empty
if self.is_empty() {
return None;
}
if self.head == self.tail {
item = unsafe { (*self.head).clone() };
self.head = ptr::null_mut();
self.tail = ptr::null_mut();
return Some(item);
}
unsafe {
item = (*self.head).clone();
self.head = self.head.offset(1);
}
return Some(item);
}
fn is_empty(&self) -> bool {
if (self.head == ptr::null_mut()) && (self.tail == ptr::null_mut()) {
return true;
}
false
}
fn is_full(&self) -> bool {
if self.tail as *const T == self.buf.end {
return true;
} else {
false
}
}
}
#[derive(Debug)]
pub enum QueueError {
Full,
}
/// struct with pointer to the start and end of the buffer
#[derive(Clone, Copy)]
struct QueueBuf<T> {
start: *const T,
end: *const T,
}
#[cfg(test)]
mod test {
use tracing::Instrument;
use crate::{Queue, QueueInterface};
#[derive(Clone)]
struct SampleStrucType {
random_string: String,
another_random_string: String,
}
#[test]
fn queue_size() {
let mut queue = Queue::<u8>::new(10);
queue.enqueue(10).unwrap();
queue.enqueue(20).unwrap();
queue.enqueue(30).unwrap();
queue.enqueue(40).unwrap();
let size = queue.size();
assert_eq!(4 as isize, size);
}
#[test]
fn empty() {
let mut queue = Queue::<u8>::new(10);
assert!(queue.is_empty())
}
#[test]
fn empty_after_complete_dequeue() {
let mut queue = Queue::<u8>::new(10);
queue.enqueue(10).unwrap();
queue.enqueue(20).unwrap();
queue.enqueue(30).unwrap();
queue.enqueue(40).unwrap();
queue.dequeue();
queue.dequeue();
queue.dequeue();
queue.dequeue();
assert!(queue.is_empty());
}
#[test]
fn dequeue() {
let mut queue = Queue::<u8>::new(10);
// fill up the queue
queue.enqueue(10).unwrap();
queue.enqueue(20).unwrap();
queue.enqueue(30).unwrap();
queue.enqueue(40).unwrap();
assert_eq!(Some(10), queue.dequeue());
assert_eq!(Some(20), queue.dequeue());
assert_eq!(Some(30), queue.dequeue());
assert_eq!(1 as isize, queue.size());
queue.enqueue(50).unwrap();
assert_eq!(2 as isize, queue.size());
}
#[test]
fn is_full() {
let mut queue = Queue::<u8>::new(4);
queue.enqueue(10).unwrap();
queue.enqueue(20).unwrap();
queue.enqueue(30).unwrap();
queue.enqueue(40).unwrap();
assert!(queue.is_full());
}
#[test]
fn enqueue() {
let task = SampleStrucType {
random_string: "STB_5500_UA".to_string(),
another_random_string: "C055566".to_string(),
};
let mut queue = Queue::<SampleStrucType>::new(10);
queue.enqueue(task).unwrap();
assert_eq!(1 as isize, queue.size());
}
}
All my test passed, no i decided to include it another project for the sake of using it
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
let subscriber = tracing_subscriber::FmtSubscriber::builder()
.with_max_level(Level::DEBUG)
.with_level(true)
.finish();
// set up our global subscriber
tracing::subscriber::set_global_default(subscriber);
// create a task Queue
let mut queue = Queue::<UpgradeTask>::new(10);
queue.enqueue(UpgradeTask { // <== Issue occur inside the function (check above for function defination)
model: "stb_5500_UA".to_string(),
navision_code: "66666".to_string(),
endpoint_type: None,
});
// ........skipped ....
Ok(())
}
Error I was getting, I also added pointer addresses it is also same and did not change. not sure if that help
2021-11-21T22:57:50.721058Z INFO queue: main_buf_ptr_address_when_created="0x555555cd69e0"
2021-11-21T22:57:56.324303Z INFO queue: address_of_ptr_head="0x555555cd69e0"
2021-11-21T22:58:01.880832Z INFO queue: address_of_ptr_tail="0x555555cd69e0"
double free or corruption (out)
Signal: SIGABRT (Aborted)
Process finished with exit code 1