Trait constraint problem: Box<dyn Item> & Default

How can I satisfy the Default trait constraint with a Box. I have a trait ItemTrait which implementer (some Item) also implements the Default trait. Then I want to add the item to a collections as follows:

  pub fn add_item(&mut self, item: &mut impl ItemTrait) {
        let mut items = IndexedCollection::<Box<&mut dyn ItemTrait>>::default();
    }

upon which I get the following error msg:

error[E0599]: no function or associated item named `default` found for struct `sxlf_base::indexed_collection::IndexedCollection<std::boxed::Box<&mut dyn sxlf_compiler::modeling::ItemTrait>>` in the current scope
   --> src/sxlf-simulator/src/simulator/main.rs:82:72
    |
82  |         let mut items = IndexedCollection::<Box<&mut dyn ItemTrait>>::default();
    |                                                                        ^^^^^^^ function or associated item not found in `sxlf_base::indexed_collection::IndexedCollection<std::boxed::Box<&mut dyn sxlf_compiler::modeling::ItemTrait>>`
    | 
   ::: /home/sx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/liballoc/boxed.rs:162:1
    |
162 | pub struct Box<T: ?Sized>(Unique<T>);
    | ------------------------------------- doesn't satisfy `_: std::default::Default`
    | 
   ::: /home/sx/workspace/sxlf/src/sxlf-base/src/indexed_collection.rs:31:1
    |
31  | pub struct IndexedCollection<T> {
    | ------------------------------- doesn't satisfy `_: std::default::Default`
    |
    = note: the method `default` exists but the following trait bounds were not satisfied:
            `std::boxed::Box<&mut dyn sxlf_compiler::modeling::ItemTrait>: std::default::Default`
            which is required by `sxlf_base::indexed_collection::IndexedCollection<std::boxed::Box<&mut dyn sxlf_compiler::modeling::ItemTrait>>: std::default::Default`

Some help much appreciated!

Can you show more of your code? Boxing an &mut reference is an extremely odd thing to do, and probably not what you want. It can also never have a Default implementation because the referent needs to be stored higher up on the stack somewhere, which Default doesn’t have access to.

Basically I'm trying to implement a callback mechanism on a heterogeneous set of objects (struct instantiations which are all known at compile time). These objects all implement the ItemTrait so I want to store them in a HashMap<String, ...some object reference...> so that at runtime I can reference these items by string and call their methods.

(Thanks for correcting me on weirdness of Boxing an &mut.)

You can hold a &mut dyn ItemTrait directly, without boxing.


Back to your issue, I suspect your struct IndexedCollection has a #[derive(Default)], on it, has it not? That will add unnecessary bounds on type parameters.

Try using ::derivative:

#[derive(Derivative)]
#[derivative(Default(bound=""))]
struct IndexedCollection<T> { ... }
  • Or don't make the collection generic over the item type:

    #[derive(Default)]
    struct IndexedCollection<'item> {
        ... // use `&'item mut dyn ItemTrait` instead of `T`
    }
    

Do note that referring to items by a lifetime-bounded reference (c.f., 'item parameter above) can be quite unergonomic. Depending on the use case, you may need to have ownership over the inserted items. In that case, use Box instead of &'_ mut:

    pub
    fn add_item(&mut self, item: impl 'static + ItemTrait) {
        let mut items = IndexedCollection::<Box<dyn ItemTrait>>::default();
        items.insert(..., Box::new(item));
    }
1 Like

You’ll need to implement Default yourself for IndexedCollection instead if deriving it— the derive macro requires all generic parameters to implement Default, but that’s not necessary to make an empty collection.

As far as the object reference goes, you’ve got a few choices:

  • &dyn ItemTrait will prevent the collection as a whole from outliving any item that’s ever added to it.
  • &mut dyn ItemTrait will do the same, but also prevent anything else from accessing any of the items while the collection exists.
  • Box<dyn ItemTrait> will let the collection wholly own the item. After it’s added, you’ll need to ask the collection for a reference anytime you need one in the future.
  • Arc/Rc<dyn ItemTrait> will let both the collection and other code have joint ownership of the object, and it will last as long as it’s referenced somewhere.
  • Weak<dyn ItemTrait> will let the collection refer to an item owned elsewhere, and will be automatically disconnected when the object is deconstructed.
1 Like