Search code examples
vectorrusttraitstrait-objects

Return Vec mut ref trait objects from Vec


I am trying to implement part of a UI framework. In the bottom example, the impl of widgets_mut() for Vec<T> needs to return a vector of trait objects like Vec<&mut dyn AnyWidget>. Given T is constrained to impl AnyWidget I don't understand why I am getting the error:

a value of type `Vec<&mut dyn AnyWidget>` cannot be built from an iterator over elements of type `&mut T`
the trait `FromIterator<&mut T>` is not implemented for `Vec<&mut dyn AnyWidget>`

Why does the 2-tuple implementation work, but the Vec implementation which seems equivalent doesn't?

fn main() {}

pub trait AnyWidget {}

pub trait WidgetVec {
    fn widgets_mut(&mut self) -> Vec<&mut dyn AnyWidget>;
}

// This implementation for a 2-tuple seems equivalent and works fine:
impl<W0: AnyWidget, W1: AnyWidget> WidgetVec for (W0, W1) {
    fn widgets_mut(&mut self) -> Vec<&mut dyn AnyWidget> {
        let mut v: Vec<&mut dyn AnyWidget> = Vec::with_capacity(2);
        v.push(&mut self.0);
        v
    }
}

impl<T: AnyWidget> WidgetVec for Vec<T> {
    fn widgets_mut(&mut self) -> Vec<&mut dyn AnyWidget> {
        self.iter_mut()
            .map(|child| child)
            .collect::<Vec<&mut dyn AnyWidget>>()
    }
}

Solution

  • All you're missing is to explicitly cast the child from &mut T to &mut dyn AnyWidget.

    impl<T: AnyWidget> WidgetVec for Vec<T> {
        fn widgets_mut(&mut self) -> Vec<&mut dyn AnyWidget> {
            self.iter_mut()
                .map(|child| child as &mut dyn AnyWidget)
                .collect::<Vec<&mut dyn AnyWidget>>()
        }
    }
    

    In some cases, rust will perform these kinds of conversions automatically (at "coercion sites"), but this can't be done here, because there isn't a coercion site where you're trying to cast child. See the reference on coercion sites for all the places this can be done.