Vector of Trait with generic method

Hi everyone,
I am learning Rust and trying to implement a small project with it.
I am facing some issues with trait that contains generic method.
Basically, I want to have a vector that contains objects of multiple types which implement a trait.
The point is that the trait has a generic method inside.


use std::collections::HashMap;
trait AwesomeTrait {
    fn awesomeness<B> (&self, x: B) {
        println!("Awesome Trait!");
    }
    
    fn boxed_clone(&self) -> Box<dyn AwesomeTrait>;
}

#[derive(Clone, Debug)]
struct AwesomeStruct;

impl AwesomeTrait for AwesomeStruct {
    fn boxed_clone(&self) -> Box<dyn AwesomeTrait> {
        Box::new(self.clone())
    }
}

#[derive(Clone, Debug)]
struct AwesomeStruct2;

impl AwesomeTrait for AwesomeStruct2 {
    fn boxed_clone(&self) -> Box<dyn AwesomeTrait> {
        Box::new(self.clone())
    }
}

#[derive(Clone)]
struct ListOfAwesomeStruct{
    list: HashMap<String, Box<dyn AwesomeTrait>>
}

impl Clone for Box<dyn AwesomeTrait> {
    fn clone(&self) -> Self {
        self.boxed_clone()
    }
}

impl ListOfAwesomeStruct {
    fn new () -> Self {
        Self {
            list: HashMap::new(),
        }
    }
    
    fn push<S: AwesomeTrait + 'static>(&mut self, key: String,awesome: S) -> &mut Self {
        self.list.insert(key, Box::new(awesome));
        
        self
    }
}

fn main() {
    let mut ooo = ListOfAwesomeStruct::new();
    ooo.push("key1".to_owned(), AwesomeStruct);
    ooo.push("key2".to_owned(), AwesomeStruct2);
}

This is the error:

error[E0038]: the trait `AwesomeTrait` cannot be made into an object
 --> src/main.rs:8:30
  |
8 |     fn boxed_clone(&self) -> Box<dyn AwesomeTrait>;
  |                              ^^^^^^^^^^^^^^^^^^^^^ `AwesomeTrait` 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>
 --> src/main.rs:4:8
  |
3 | trait AwesomeTrait {
  |       ------------ this trait cannot be made into an object...
4 |     fn awesomeness<B> (&self, x: B) {
  |        ^^^^^^^^^^^ ...because method `awesomeness` has generic type parameters
  = help: consider moving `awesomeness` to another trait

Is there any solution to solve this issue? Thank you in advance.

If you do not need to call awesomeness() on the dyn AwesomeTrait, you can restrict it to where Self: Sized to make the trait object safe:

fn awesomeness<B>(&self, x: B)
where
    Self: Sized,
{
    // ...
}

Another alternative is to type-erase the generic parameter on your method, too.

trait CanAwe {}
// ^^^^^^^^^^^^

use std::collections::HashMap;
trait AwesomeTrait {
    fn awesomeness(&self, x: Box<dyn CanAwe>) {
//                           ^^^^^^^^^^^^^^^
        println!("Awesome Trait!");
    }
    
    fn boxed_clone(&self) -> Box<dyn AwesomeTrait>;
}

Playground.

1 Like

Object safety is pretty confusing! It doesn't help the error looks you to a pretty dense specification of what it is, but not why or how to fix it. The suggested fix is ok, but not if you actually do need to call it!

1 Like

Thanks for your suggestion. Indeed I need to call the method awesomeness() on dyn AwaresomeTrait

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.