2

how can I create a copy of a trait object for local calculations?

The problem is that I want to have a function that takes a mutable reference to a trait object in order to do some calculations and modify it based on those calculations, but during the calculations, the function has to modify the trait object temporarily and locally to do calculations with the modified version of it.

I've tried the following (and variations):

struct Data {
    fields: Vec<f32>
}

trait CustomCollection {
    fn get_mut(&mut self) -> &mut [f32];
}

impl CustomCollection for Data {
    fn get_mut(&mut self) -> &mut [f32] {
    return &mut self.fields;
    }
}

fn copy(s: &mut dyn CustomCollection) {
    let c: Box::<dyn CustomCollection> = Box::new(*s);
}

fn main() {
    let mut d = Data{fields: vec![1f32, 2.0, 3.0]};
    copy(&mut d);
}

but I get an error:

error[E0277]: the size for values of type `dyn CustomCollection` cannot be known at compilation time
  --> src/test.rs:16:51
   |
16 |     let c: Box::<dyn CustomCollection> = Box::new(*s);
   |                                          -------- ^^ doesn't have a size known at compile-time
   |                                          |
   |                                          required by a bound introduced by this call
   |
   = help: the trait `Sized` is not implemented for `dyn CustomCollection`
note: required by a bound in `Box::<T>::new`
  --> /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/alloc/src/boxed.rs:204:6
   |
   = note: required by this bound in `Box::<T>::new`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
user3617992
  • 359
  • 1
  • 8

1 Answers1

2

You can extend the trait you want to build trait objects from with a clone or copy method which returns Box<dyn CustomCollection>. Like this, for example:

#[derive(Clone)]
struct Data {
    fields: Vec<f32>
}

trait CustomCollection {
    fn get_mut(&mut self) -> &mut [f32];
    
    fn clone_box(&self) -> Box<dyn CustomCollection>;
}

impl CustomCollection for Data {
    fn get_mut(&mut self) -> &mut [f32] {
        &mut self.fields
    }
    
    fn clone_box(&self) -> Box<dyn CustomCollection> {
        Box::new(<Self as Clone>::clone(self))
    }
}

fn copy(s: &mut dyn CustomCollection) -> Box<dyn CustomCollection> {
    s.clone_box()
}

fn main() {
    let mut d = Data{fields: vec![1f32, 2.0, 3.0]};
    copy(&mut d);
}

Playground.


@prog-fh was so kind and pointed this answer out, which contains a more flexible implementation of what I tried to accomplish in my example. There is also the dyn-clone crate by @dtolnay readily available for you to use, so you don't have to implement cloning trait objects yourself.

Jonas Fassbender
  • 2,371
  • 1
  • 3
  • 19
  • 2
    I would have chosen the name `clone_box()` instead of `clone()`, in order to make clear that this is not a direct implementation of the standard `Clone` trait but something related, like in [this answer](https://stackoverflow.com/a/30353928/11527076). – prog-fh Feb 25 '23 at 09:48
  • Great point, I renamed the method. The answer you linked contains a much more powerful and flexible implementation than mine, though I think mine is easier to reason about for people not yet well accustomed to Rust's trait system. Can I add the link to my answer as "for a more powerful way to construct clones of trait objects see this answer"? – Jonas Fassbender Feb 25 '23 at 10:09
  • 1
    Yes of course, add anything you want to your answer as long as you think that improves it and makes it more useful. – prog-fh Feb 25 '23 at 11:10