Passing trait object to a thread

I'm trying to pass a trait object to a thread, so that a library can accept a trait object that defines some functions.

pub trait AssetFetcher: Sync {
    //  Callbacks, implemented by user of trait.
    fn fetch_texture_images(
        &self,
        req: &AssetRequest,
        asset_timing: &mut AssetTiming,
    ) -> Result<TextureImages, Error>;
    
    fn fetch_mesh(&self); // more to come
}

If I try to pass this by value, the compiler complains "doesn't have a size known at compile-time", which is reasonable enough. If I try to pass it by reference, I get "explicit lifetime required in the type of asset_fetcher". If I try to make a copy, I get "Lifetime 'static required" at the spawn. Also reasonable.

So I need to make a clone. But that doesn't seem to be allowed. If I make the trait "Sync+Clone", I get

note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> src/render/renderdefs.rs:76:30
   |
76 | pub trait AssetFetcher: Sync+Clone {
   |           ------------       ^^^^^ ...because it requires `Self: Sized`
   |           |
   |           this trait cannot be made into an object..

So "clone" requires "Sized", which is reasonable enough.

All this seems to add up to "cannot pass a non-static trait object to a thread". Is that correct?

Have you tried sending Box<dyn AssetFetcher> between threads? That's effectively an owned trait object which can be moved between threads. It should work unless there's something else going on that I'm not seeing. (You might need to make the trait require Send as well)

playground example

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=94d1cfdecb5931c35ae13775ebcf02dd

That lets me move it, but I can't clone it for use in multiple threads and then move a boxed clone. If it's cloneable, it's not boxable.

 ^^^^^ method cannot be called on `Box<dyn Foo>` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `dyn Foo: Sized`
            which is required by `Box<dyn Foo>: Clone`
            `dyn Foo: Clone`
            which is required by `Box<dyn Foo>: Clone`

Do you need a "deep clone" or just multiple owning handles to one object? If the former, dyn-clone may help. If the latter, you want Arc<dyn Foo + Send + Sync>. (Possibly with some interior mutability in the mix.)

1 Like

You can't clone trait objects. Clone trait is incompatible with them, because it returns a new object by value, and trait object can't be passed by value.

Use Arc<dyn Trait> if you need a cloneable trait object (all clones will share the same object). If you need separate instances of the object, clone concrete types them before making them dyn.

Ah, that works. Boxing does not help, but encapsulating with an Arc does.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.