How do I store a struct that owns an `impl Trait` in another struct?

I am using a crate which has a struct (lets call it ChosenContainer ) that I want to use. I want to store a ChosenContainer in my OwnContainer struct. But I am not sure what the type of the stored value should be.

I made a small code snippet which corresponds to my situation. I tried all different types in my OwnContainer.

  • I tried Boxing the ChosenContainer struct: c_container: Box<ChosenContainer<dyn Chosen>>
  • I tried trait bounds c_container: ChosenContainer<T> where T: Chosen
  • I tried combining them.
  • Googled for a few hours.

I have no idea how to do this. Is this even possible?

// --- code from a foreign crate simplified below --- //
pub struct Thing;

pub trait Chosen {}
impl Chosen for Thing {}

pub fn get_chosen() -> impl Chosen {
    Thing
}

pub struct ChosenContainer<C: Chosen> {
    chosen: C,
}

impl<C: Chosen> ChosenContainer<C> {
    pub fn new(chosen: C) -> Self {
        ChosenContainer { chosen }
    }
}

// --- my code so we can edit stuff below --- //

// should own a ChosenContainer
struct OwnContainer {
    c_container: ChosenContainer<dyn Chosen>,
}

impl OwnContainer {
    fn new() -> Self { // I want to keep function signiture
        let c = get_chosen();
        let c_container = ChosenContainer::new(c);
        OwnContainer {
            c_container,
        }
    }
}

fn main() {
    // should be able to create one like this:
    let own_container = OwnContainer::new();
}

Any help would be greatly appreciated.

The two road bumps here appear to be that you can't name opaque types such as the one returned from get_chosen(), and that Box<dyn Trait> doesn't implement Trait. You can work around the second one using a new type, assuming you're able to impl Chosen. Here's a sketch based off your example on the playground.

1 Like

Thanks for the quick answer. I noticed that I can store the Chosen in a struct like that but didn't think to use that as a wrapper. Thanks again!

1 Like

You don’t need the newtype here; you can implement Chosen for Box<T> directly:


impl<T:Chosen+?Sized> Chosen for Box<T> {
    // delegate to T
}

(Playground)

I assumed the orphan rule would apply due to the notes about a foreign crate.

2 Likes

As @quinedot pointed out the compiler nicely tells me that "implementing a foreign trait is only possible if at least one of the types for which is it implemented is local"

This also works, but it’s a bit weird and attaches a type parameter to OwnContainer:

// should own a ChosenContainer
struct OwnContainer<C:Chosen> {
    c_container: ChosenContainer<C>,
}

// `Thing` is any arbitrary type that implements `Chosen`;
// It’s only here to make type inference work nicely.
impl OwnContainer<Thing> {
    fn new() -> OwnContainer<impl Chosen> {
        // I want to keep function signiture
        let chosen = get_chosen();
        let c_container = ChosenContainer::new( chosen );
        OwnContainer { c_container }
    }
}

fn main() {
    // should be able to create one like this:
    let own_container = OwnContainer::new();
}

(Playground)


On nightly, we have the type_alias_impl_trait feature which makes this a bit nicer: we can define a default inner type that lets us skip the type parameter and we can use that alias in our impl block:

#![feature(type_alias_impl_trait)]
type DefaultChosen = impl Chosen;

// should own a ChosenContainer
struct OwnContainer<C:Chosen = DefaultChosen> {
    c_container: ChosenContainer<C>,
}

impl OwnContainer<DefaultChosen> {
    fn new() -> Self {
        // I want to keep function signiture
        let chosen = get_chosen();
        let c_container = ChosenContainer::new( chosen );
        OwnContainer { c_container }
    }
}

fn main() {
    // should be able to create one like this:
    let own_container = OwnContainer::new();
}

(Playground)

1 Like

Interesting for sure :slight_smile: