Use name of a trait as a type parameter

Summary

I wanted to write a into_trait to cast a smart pointer of type Rc<ConcreteType> into Rc<dyn Trait>

fn into_trait<T, U>(concrete: Rc<T>) -> Rc<dyn U> where T: U;

The above function won't compile. Is there any way to do this?

Context

I was writing a wrapper for Rc. I noticed that Rc<ConcreteType> can be converted into Rc<dyn Trait>. As far as I understood, this could be done because Rc implemented CoerceUnsized, which was unstable now. I was trying to write a function (rather than a macro) to convert Wrapper<Concrete> into Wrapper<dyn Trait>.

Code

The following code was also put in the playground.

use std::{rc::Rc, cell::RefCell, any::Any};

struct Wrapper<T: ?Sized + 'static> {
    inner: Rc<T>
}

impl<T> Wrapper<T> {
    // this is fine
    fn into_any(self) -> Wrapper<dyn Any> {
        Wrapper { inner: self.inner }
    }

    // -----> how can I achieve this? <-----
    // It's something can be done with std::is_base_of in C++, 
    // but I don't know how to do it in Rust
    fn into_trait<U>(self) -> Wrapper<dyn U> where T: U {
        Wrapper { inner: self.inner }
    }
}

fn main() {
    let w = Wrapper { inner: Rc::new(RefCell::new(1)) };
    let w = w.into_trait::<Any>();  // <---- would be called here
}

You cannot use a generic type to name a trait, because a trait is not a type. The unsize crate provides a stable way to name unsizing coercions and use them generically.

3 Likes

You'd have to do it like this:

fn into_trait<U: ?Sized + 'static>(self) -> Wrapper<U>
where T: Unsize<U> {
    Wrapper { inner: self.inner }
}

and call it with a type dyn Any instead of a Trait:

let w = w.into_trait::<dyn Any>();

Sadly, Unsized is also unstable. :frowning:

Thanks for your suggestion. I am going to try this. I used to regard this as a language feature. I am also pretty curious about its implementation. It seems that the crate has a really neat codebase and I will look into it.