0

I have the following code:

pub trait Service {}

#[derive(Clone)]
pub struct Services {
    providers: std::collections::HashMap<String, Box<dyn Service>>
}

which gives this error:

the trait `Clone` is not implemented for `dyn Service`

I also tried changing Service to pub trait Service: Clone {} but it gave me a different error that the trait cannot be made into an object because it is not object_safe (because it requires Self::Sized.

playground - https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=eecca2b2bd82d4df4c34f44463594c58

ferd tomale
  • 845
  • 7
  • 20
  • Does this answer your question? [How to clone a struct storing a boxed trait object?](https://stackoverflow.com/questions/30353462/how-to-clone-a-struct-storing-a-boxed-trait-object) – Filipe Rodrigues Sep 27 '21 at 10:42

2 Answers2

2

I'm afraid that's not gonna happened, if you look at the definition of Clone, it is required to Sized, but a trait object is not sized, you can't implement Clone for it.

you can wrap the provide in the Rc or Arc like this

#[derive(Clone)]
pub struct Services {
  providers: Arc<std::collections::HashMap<String, Box<dyn Service>>>
}
Sean
  • 2,990
  • 1
  • 21
  • 31
0

You can add Clone in the definition of the type that is enclosed in Box.

Something like this

use std::collections::HashMap;

pub trait Service {}

#[derive(Clone)]
pub struct Services<T>
    where
        T: Service, //<--------
        T: Clone //<----------- {
    providers: std::collections::HashMap<String, Box<T>>
}

#[derive(Clone)]
pub struct A{}

impl Service for A {}

fn main()
{
    let mut v: HashMap<String, Box<A>> = HashMap::new();
    v.insert("test".to_string(), Box::new(A{}));
    
    let o = Services{providers: v };
    
    for (key, _) in o.providers.iter() {
        println!("key: {} ", key);
    }
}

The Sean version could be like this

use std::collections::HashMap;
use std::sync::Arc;

pub trait Service {}

#[derive(Clone)]
pub struct Services {
    providers: Arc<std::collections::HashMap<String, Box<dyn Service>>>
}

#[derive(Clone)]
pub struct A{}
impl Service for A {}

#[derive(Clone)]
pub struct B{}
impl Service for B {}

fn main()
{
    let mut objects: HashMap<String, Box<dyn Service>> = HashMap::new();
    objects.insert("test".to_string(), Box::new(A{}));
    objects.insert("test2".to_string(), Box::new(B{}));
    
    let v = Arc::new(objects);
    let o = Services{providers: v};
    
    for (key, _) in o.providers.iter() {
        println!("key: {} ", key);
    }
}
Zeppi
  • 1,175
  • 6
  • 11
  • it's quite different, you now make it static dispatch instead of dynamic dispatch – Sean Sep 24 '21 at 14:25
  • Hello, i think it was marked , because her need is to define a generic type with trait Service and compiler require to mark trait objects with an explicit `dyn`. But, now. Your also true. I dont know waht is her final goal. – Zeppi Sep 24 '21 at 14:52
  • it depend on how he gonna use the hashmap, if what he want is to put different types which impl the same trait in the map, that it has to be a trait-object – Sean Sep 24 '21 at 15:22
  • I want to put different types that impl the Service trait into the hashmap. – ferd tomale Sep 24 '21 at 22:20
  • Hello @ferd, in this case this solution not work. You have to implement wath Sean has proposed. – Zeppi Sep 25 '21 at 06:20