I am attempting to implement a queue where producers can add a struct in that implements QueueTrait and when dequeued the invoke method of the trait will be called on a different thread. This does not compile due to the following error:
self.queue.enqueue(self.data);
^^^^^^^^^ expected trait object `dyn QueueTrait`, found struct `ProducerData`
expected struct `std::sync::Arc<std::boxed::Box<dyn QueueTrait + std::marker::Send>>`
found struct `std::sync::Arc<std::boxed::Box<ProducerData>>
Is it possible to cast ProducerData to QueueTrait? Is there any way to avoid splitting Producer into ProducerData altogether and simply add Producer itself into the queue?
use std::collections::VecDeque;
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
pub struct Queue<'a> {
items: Mutex<VecDeque<Arc<Box<dyn QueueTrait + Send + 'a>>>>,
condvar: Condvar,
}
impl<'a> Queue<'a> {
pub fn new() -> Self {
Self {
condvar: Condvar::new(),
items: Mutex::new(VecDeque::new()),
}
}
pub fn enqueue(&self, qt: Arc<Box<dyn QueueTrait + Send + 'a>>) {
self.items.lock().unwrap().push_back(qt);
self.condvar.notify_one();
}
fn dequeue(&self) -> Arc<Box<dyn QueueTrait + Send + 'a>> {
let mut q = self.items.lock().unwrap();
while q.is_empty() {
q = self.condvar.wait(q).unwrap();
}
q.pop_front().unwrap()
}
pub fn dispatch(&self) {
loop {
let qt = self.dequeue();
qt.invoke();
}
}
}
struct ProducerData {
invoke_count: Mutex<u32>
}
struct Producer {
queue: Arc<Queue<'static>>,
data: Arc<Box<ProducerData>>
}
impl Producer {
fn new(queue: Arc<Queue<'static>>) -> Self {
Self { queue, data: Arc::new(Box::new(ProducerData{ invoke_count: Mutex::new(0)}) ) }
}
fn produce(&self) {
self.queue.enqueue(self.data);
}
}
pub trait QueueTrait {
fn invoke(&self);
fn as_trait(&self) -> &dyn QueueTrait;
}
impl QueueTrait for ProducerData {
fn invoke(&self) {
self.invoke_count.lock().unwrap() +=1;
println!("Invoke called!")
}
fn as_trait(&self) -> &dyn QueueTrait {
self as &dyn QueueTrait
}
}
fn main() {
let q = Arc::new(Queue::new());
let p1 = Producer::new(Arc::clone(&q));
// Consumer thread
let c = thread::Builder::new().name("consumer".to_string()).spawn(move
|| q.dispatch() ).unwrap();
// Produce on main thread
p1.produce();
c.join().unwrap();
}
You used casting already in as_trait
but just to make it clearer. Casting an object to a trait can be done with the as
keyword:
use std::sync::Arc;
struct MyStruct;
trait MyTrait {}
impl MyTrait for MyStruct {}
fn main() {
let my_struct: MyStruct = MyStruct;
// behind a reference
let trait_object: &dyn MyTrait = &MyStruct as &dyn MyTrait;
// behind a box
let trait_object: Box<dyn MyTrait> = Box::new(MyStruct) as Box<dyn MyTrait>;
// behind an Arc
let trait_object: Arc<dyn MyTrait> = Arc::new(MyStruct) as Arc<dyn MyTrait>;
// behind an Arc Box
// DOESN'T COMPILE!
let trait_object: Arc<Box<dyn MyTrait>> = Arc::new(Box::new(MyStruct)) as Arc<Box<dyn MyTrait>>;
}
But it doesn't work behind two indirections for example Arc
and Box
.
This whole setup you have seems very overcomplicted to me. As pointed out by @kmdreko using just Arc
seems to work. You should re-think what your program is trying to do and think of a simpler way.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.