Holding Arc<Box<TraitImpl>> and Arc<Box<dyn Trait>>

Hello community,

I am pretty sure that this could work but I haven't figuered out how to implement it. Maybe one of you can lead me in the rigtht direction.

The Problem

I want to manage some objects that are hold by some kind of World. This struct has a Vec<Arc<Mutex<Box<dyn Trait>>>>. The World's task is to hand out Arcs to worker threads that depend on these Trait-objects. However, some threads require the concrete struct behind the trait-objects.
As far as I know, it is currently not possible to downcast a trait-object to its struct but this is not exactly my problem. The trait-objects are created together with the threads that require the concrete structs. My problem is how do I manage that one thread refers to Arc<Mutex<Box<TraitImpl>>> and hand an Arc::clone() of that as Arc<Mutex<Box<dyn Trait>>> to the World?

1 Like

Yeah, there's some limited support for downcasting if you use Any, e.g. Box<Any>.

I don't think you'll be able to do this because Box<TraitImpl> and Box<dyn Trait> have a different representation (layout). In theory, a Mutex shouldn't care whether it's holding a Box<TraitImpl> or Box<dyn Trait + Send>, but we'd need machinery to allow such an unsized coercion via Mutex. I also may be overlooking something as I've not thought too deeply about this ...

At any rate, a typical way to allow downcasting like this is to put the downcast operation straight into the trait, e.g.:

trait Trait {
    fn as_impl(&self) -> Option<&TraitImpl> {
        None
    }
    
}

struct TraitImpl;

impl Trait for TraitImpl {
    fn as_impl(&self) -> Option<&TraitImpl> {
        Some(self)
    }
}

Then you store the trait object everywhere, and places that need access to TraitImpl go via as_impl.

Also, have you considered using an enum instead of trait objects? Do you expect to have an open set of possible trait impls?

Can you get rid of the inner Box in any way? I haven't tested it, but I'm 95% sure Arc<Mutex<TraitImpl>> can be coerced to Arc<Mutex<dyn Trait>>. The Box doesn't seem like it should be necessary.

2 Likes

this is what I've done meanwhile. I realized that there will never be more than two impls of the trait and switched to an enum.
Sometimes it is hard to get rid of this OOP-thinking of using dynamic dispatch everywhere.

Yes, the box should not be necessary as of something like rust 1.5.

Oh, indeed that's a good thought. For some reason, I thought the Mutex inside the Arc would preclude the unsizing but Mutex does allow T: ?Sized. So that "machinery" I alluded to is already present for this case.

Yeah, Rust enums are, IMO, one of its best features :slight_smile:. Your case can be made to work, given the rest of the discussion in this thread, but I personally avoid trait objects as much as possible as sooner or later you hit some issues with them, mostly dependent on the type of trait though (i.e. generic lifetime param or not, associated type or not, etc).