AlphaBeta
AlphaBeta

Reputation: 320

Multible trait objects of the same instance wrapped by Arc<Mutex<_>>

The goal is to have an object (callback_handler) implementing the traits A, B and C that can be passed to different functions that expect a trait object of type A or B, for instance.

This requires callback_handler, and the trait objects to be protected by the same Mutex (since, e.g., trait A expects &mut self). Furthermore, for this setup to work, this Mutex must be wrapped in an Arc or Rc:

use std::sync::{Arc, Mutex};

trait A {
    fn a(&mut self) {}
}
trait B {}
trait C: A + B {}

struct Foo {}

impl A for Foo {}
impl B for Foo {}
impl C for Foo {}

fn register_for_A(callback_handler: Arc<Mutex<Box<dyn A>>>) {
    // Register for callbacks defined in `A`.
    // Store the passed handler somewhere.
}

fn register_for_B(callback_handler: Arc<Mutex<Box<dyn B>>>) {
    // Register for callbacks defined in `B`.
    // Store the passed handler somewhere.
}

fn main() {
    let callback_handler = Arc::new(Mutex::new(Box::new(Foo{})));
    // Register for callbacks using different trait objects of the same "origin" object.
    // For this to be safe, all objects must be protected by the same ("shared") mutex.
    // !!! This will not work since there must be some steps to cast this to the right type. !!!
    register_for_A(Arc::clone(&callback_handler));
    register_for_B(Arc::clone(&callback_handler));

    // We can still use `callback_handler` to call methods on Foo ourself
}

Now, the question is, how can the origin object callback_hanlder of type Arc<Mutex<Box<Foo>>> be casted/converted to an Arc<Mutex<Box<dyn A>>> and Arc<Mutex<Box<dyn B>>>? While I don't see a technical reason why this should not be possible, I don't know how to perform such a conversion and whether it is doable. While one solution would be using the Any trait, I was hoping that there is a solution that retains compile-time type safety for functions register_for_A and register_for_B.

Upvotes: 0

Views: 201

Answers (1)

cafce25
cafce25

Reputation: 27303

You're getting tripped up by your double use of smart pointers Box + Arc you can simply cast an Arc<Mutex<Foo>> to an Arc<Mutex<dyn A>> using as, there is really no need to additionally Box it:

use std::sync::{Arc, Mutex};

trait A {
    fn a(&mut self) {}
}
trait B {}

struct Foo {}

impl A for Foo {}
impl B for Foo {}

fn register_for_A(callback_handler: Arc<Mutex<dyn A>>) {
    // Register for callbacks defined in `A`.
    // Store the passed handler somewhere.
}

fn register_for_B(callback_handler: Arc<Mutex<dyn B>>) {
    // Register for callbacks defined in `B`.
    // Store the passed handler somewhere.
}

fn main() {
    let callback_handler = Arc::new(Mutex::new(Foo{}));
    // Register for callbacks using different trait objects of the same "origin" object.
    // For this to be safe, all objects must be protected by the same ("shared") mutex.
    // !!! This will not work since there must be some steps to cast this to the right type. !!!
    register_for_A(Arc::clone(&callback_handler) as _);
    register_for_B(Arc::clone(&callback_handler) as _);

    // We can still use `callback_handler` to call methods on Foo ourself
}

Upvotes: 1

Related Questions