Vector of Generic struct (where the generic is a trait)

Hi everybody,

Today, I come back with another question :thinking:

In fact, I'm trying to store a Vec of a generic struct (Library), where the generic is a trait (LibraryElement, like books, DVD, ...).
My vector should be able to store all the type of Library struct (so books, etc...).

I have already see some questions like this :

But I am not really sure that it correspond to my problem.

Here the code
pub trait LibraryElementEntity {}

#[derive(Clone)]
pub struct LibraryEntity<T: LibraryElementEntity> {
    name: String,
    elements: Vec<T>,
}

impl<T: LibraryElementEntity> LibraryEntity<T> {
    pub fn new(name: String, elements: Vec<T>) -> Self {
        Self {
            name,
            elements,
        }
    }

    pub fn get_name(&self) -> String { self.name.clone() }

    // TODO : pub fn get_elements(&self) -> Vec<T> { self.elements.to_vec() }
}

#[derive(Clone)]
pub struct UserEntity {
    id: String,
    first_name: String,
    last_name: String,
    email: String,
    password: String,
    libraries: Vec<LibraryEntity<dyn LibraryElementEntity>>, // Here the problem
}
Here the error
error[E0277]: the size for values of type `(dyn LibraryElementEntity + 'static)` cannot be known at compilation time
  --> domain\src\entities\user_entity.rs:12:16
   |
12 |     libraries: Vec<LibraryEntity<dyn LibraryElementEntity>>,
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   | 
  ::: domain\src\entities\library_entities\library_entity.rs:5:26
   |
5  | pub struct LibraryEntity<T: LibraryElementEntity> {
   |                          - required by this bound in `LibraryEntity`
   |
   = help: the trait `Sized` is not implemented for `(dyn LibraryElementEntity + 'static)`
help: consider relaxing the implicit `Sized` restriction
   |
5  | pub struct LibraryEntity<T: LibraryElementEntity + ?Sized> {
   |                                                  ^^^^^^^^

So do I need to do an enum in place of my my LibraryElementEntity trait ?
Or is it possible to do something with this trait ? (it must implement Clone, and may have common methods)

Thanks :slight_smile:

Just box it :

pub struct UserEntity {
    id: String,
    first_name: String,
    last_name: String,
    email: String,
    password: String,
    libraries: Vec<LibraryEntity<Box<dyn LibraryElementEntity>>>,
}

And, as a general rule, avoid putting trait constraints on struct definitions :

pub struct LibraryEntity<T> {
    name: String,
    elements: Vec<T>,
}
3 Likes

With regards to the 'box it' approach (which I agree is almost certainly what you want to do here), here's a pretty good article on the subject:

Well it's a bit long in-depth, but potentially interesting nonetheless :slightly_smiling_face:.

2 Likes

Thanks for your answers :slight_smile:
In fact I have tryied with Box, but I have not put it in the right place ^^'

But now I have a problem with the Clone trait now :

pub struct UserEntity {
    // ...
    libraries: Vec<LibraryEntity<Box<dyn LibraryElementEntity>>>,
}

pub trait LibraryElementEntity: Clone {}
// pub trait LibraryElementEntity: Clone where Self: Sized {} (this does not work)
Error message
error[E0038]: the trait `LibraryElementEntity` cannot be made into an object
  --> domain\src\entities\user_entity.rs:12:16
   |
12 |     libraries: Vec<LibraryEntity<Box<dyn LibraryElementEntity>>>,
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `LibraryElementEntity` cannot be made into an object
   |
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>
  --> domain\src\entities\library_entities.rs:6:33
   |
6  | pub trait LibraryElementEntity: Clone where Self: Sized {}
   |           --------------------  ^^^^^             ^^^^^ ...because it requires `Self: Sized`
   |           |                     |
   |           |                     ...because it requires `Self: Sized`
   |           this trait cannot be made into an object...

The last time I have met this type of error, I gave up an rewriting all the code :confused:

The Clone trait is not object-safe, so you won't be able to clone a trait object.

As a workaround, you can (first remove the Clone bound on your trait) use Rc instead of Box :

Vec<LibraryEntity<Rc<dyn LibraryElementEntity>>>

Clone is used as an example in the book Chapter 17.2.

For cloning trait objects, this may be of help:

Note that Rc<dyn Trait> (or its thread-safe counterpart: Arc<dyn Send + Sync + Trait>) and using ::dyn_clone solve quite different objectives:

  • with the former, you simply (and cheaply) solve lifetime / drop-order related issues, thanks to reference-counting allowing you to retain ownership of a shared instance (so as to ensure the instance itself outlives all the Rc<…> pointers) in a completely non-borrowing fashion;

    => rc sharing: cheap clones at the cost of sacrificing &mut methods ("no mutation").

  • with the latter, you won't be cloning just for lifetimes / drop reasons, you'll be cloning to actually get new fresh "copies" of the value, which you can mutate independently of the original.

    => dyn cloning: more expensive clones but you get to use &mut methods easily.

Given that in the OP I only see &self-based methods, I suggest Rc<dyn …> be used over dyn_clone

1 Like

Thanks all for your answers.

I have already use RC, and forget this tips :shushing_face:

I hope to get used to the language and its specificities over time because I really like the mentality and the functionalities implemented