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

62 Views Asked by At

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));
}
1

There are 1 best solutions below

1
Finomnis On BEST ANSWER

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)));
}