Search code examples
rustwrappertrait-objects

Clone custom structs of concrete type as trait objects


Using Rc, I can cast an Rc of a concrete type to a trait object:

use std::rc::Rc;

trait Foo {}

impl Foo for usize {}

fn main() {
    let x: Rc<usize> = Rc::new(1);
    let y: Rc<dyn Foo> = x.clone();
}

Playground.

If I define a wrapper for an Rc without the Sized bound, I can also use trait objects:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper<T: ?Sized>(Rc<T>);


impl Foo for usize {}

fn main() {
    let x: Wrapper<dyn Foo> = Wrapper(Rc::new(1));
}

Playground.

However, I cannot clone a wrapper of a concrete type as a trait object:

use std::rc::Rc;

trait Foo {}

#[derive(Clone)]
struct Wrapper<T: ?Sized>(Rc<T>);


impl Foo for usize {}

fn main() {
    let x: Wrapper<usize> = Wrapper(Rc::new(1));
    let y: Wrapper<dyn Foo> = x.clone(); // this does not compile
}

The compiler complains with the following error:

error[E0308]: mismatched types
  --> src/main.rs:13:31
   |
13 |     let y: Wrapper<dyn Foo> = x.clone();
   |            ----------------   ^^^^^^^^^ expected trait object `dyn Foo`, found `usize`
   |            |
   |            expected due to this
   |
   = note: expected struct `Wrapper<dyn Foo>`
              found struct `Wrapper<usize>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Playground.

I'm a little bit confused, as I don't understand why the third example does not work. Can anyone help me to get the third example working? what am I missing?


Solution

  • You can implement your own version of coercion to an unsized type by implementing CoerceUnsized for your Wrapper:

    #![feature(unsize, coerce_unsized)]
    use std::ops::CoerceUnsized;
    use std::marker::Unsize;
    impl<U: ?Sized, T: Unsize<U>> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
    

    then the syntax you used initially does compile

    fn main() {
        let x: Wrapper<usize> = Wrapper(Rc::new(1));
        let y: Wrapper<dyn Foo> = x.clone();
    }
    

    But since you did not include that implementation the compiler doesn't know that your type can be unsized.

    Note: Right now that requires the features unsize and coerce_unsized though so you have to compile with a nightly compiler.