Search code examples
rusttraitstrait-objects

Blanket `impl Trait1` for all types which `impl Trait2` and functions which return `impl Trait2`


I want to implement HasChildren for all types which implement Element and for all functions which return impl Element.

My case use is that I have a Vec<Box<dyn HasChildren>> in which I want to be able to store both types which implement Element and functions which return impl Element.

Worth noting that my wider use case requires using trait objects, so a solution eg using enums is not possible.

trait Element {}

trait HasChildren {}

impl<T: 'static + Element + Clone> HasChildren for T {}

// conflicting implementation
impl<F, E: Element> HasChildren for F where F: FnMut() -> E {}

// Types which I would like to use as HasChildren trait objects
#[derive(Clone)]
struct Foo {}
impl Element for Foo {}

fn get_element() -> impl Element {
    Foo {}
}

// My use case - storing both the above types in a Vector
fn main() {
    let mut data: Vec<Box<dyn HasChildren>> = Vec::new();
    data.push(Box::new(Foo {}));
    data.push(Box::new(get_element));
}

Solution

  • You could use the newtype pattern:

    use std::ops::{Deref, DerefMut};
    
    trait Element {}
    
    trait HasChildren {}
    
    // Newtype wrapper for `FnMut() -> E`
    struct ElementFn<F: FnMut() -> E, E: Element>(F);
    impl<F: FnMut() -> E, E: Element> Deref for ElementFn<F, E> {
        type Target = F;
    
        fn deref(&self) -> &F {
            &self.0
        }
    }
    impl<F: FnMut() -> E, E: Element> DerefMut for ElementFn<F, E> {
        fn deref_mut(&mut self) -> &mut F {
            &mut self.0
        }
    }
    
    // conflicting implementation
    impl<T: Element + Clone> HasChildren for T {}
    impl<F: FnMut() -> E, E: Element> HasChildren for ElementFn<F, E> {}
    
    // Types which I would like to use as HasChildren trait objects
    #[derive(Clone)]
    struct Foo {}
    impl Element for Foo {}
    
    fn get_element() -> impl Element {
        Foo {}
    }
    
    // My use case - storing both the above types in a Vector
    fn main() {
        let mut data: Vec<Box<dyn HasChildren>> = Vec::new();
        data.push(Box::new(Foo {}));
        data.push(Box::new(ElementFn(get_element)));
    }