简体   繁体   中英

Casting struct to trait

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM