Allow either T: MyTrait or Rc<T: MyTrait> for data structure element type?

I have a data structure which I'd like to fill with either all T or all Rc<T> elements, depending on the instance of the data structure. In either case, it is necessary for MyTrait to be implemented for the element type in order for the data structure to function properly.

For example, in the code below, MyCollection imposes the trait bound MyTrait on its contents type T. Instantiating MyCollection::<MyItem> succeeds (collection1 in main), because MyItem implements MyTrait.

However, attempting to instantiate a MyCollection::<Rc<MyItem>> (collection2 in main) produces the compilation error the trait MyTrait is not implemented for std::rc::Rc<MyItem>.

use std::rc::Rc;

struct MyItem {}

trait MyTrait {}

impl MyTrait for MyItem {}

struct MyCollection<T: MyTrait> {
    contents: T
}

fn main() {
    let collection1 = MyCollection::<MyItem> {
        contents: MyItem {}
    };
    
    let collection2 = MyCollection::<Rc<MyItem>> {
        contents: Rc::new(MyItem {})
    };
}
error[E0277]: the trait bound `std::rc::Rc<MyItem>: MyTrait` is not satisfied
  --> src/main.rs:39:23
   |
30 | struct MyCollection<T: MyTrait> {
   | ------------------------------- required by `MyCollection`
...
39 |     let collection2 = MyCollection::<Rc<MyItem>> {
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyTrait` is not implemented for `std::rc::Rc<MyItem>`

I suspect I approached the problem this way because I don't understand a larger concept of the language. But, for better or worse, with what I know so far, my question would be:

is there a Rusty way to impose a trait bound on the element type of a data structure, and enable the user the option of whether to wrap that type in an Rc?

How about this?

struct MyCollection<T> {
    contents: T,
}

trait MyTrait;

impl<T> MyCollection<T> {
    fn new<U: MyTrait>(contents: T) -> Self
    where
        T: AsRef<U>,
    {
        Self { contents }
    }
}

(In general it's considered good style to leave trait bounds off generic structs when possible since you have to specify them in impl blocks anyway.)

2 Likes

Another approach would be, if that’s possible, to define a generic trait implementation for Rc, e.g.

impl<T: MyTrait> MyTrait for Rc<T> {

}
3 Likes