Reputation: 320
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
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