rusttraitsdynamic-dispatch

Mutable borrow on extended trait


I have a vector of dynamic dispatch references that implement an extended trait. I would also like to be able to pass them in to a function that accepts the base trait:

use std::borrow::BorrowMut;

trait MyTrait {
    fn doit(&mut self);
}

trait MyOtherTrait: MyTrait {
    fn doit_too(&mut self);
}

fn doit<T>(items: &mut [T])
where T: BorrowMut<dyn MyTrait>
{
    for item in items.iter_mut() {
        item.borrow_mut().doit();
    }
}

struct A {}

impl MyTrait for A {
    fn doit(&mut self) { println!("AAAAA!"); }
}

impl MyOtherTrait for A {
    fn doit_too(&mut self) { println!("22222!"); }
}

fn main() {
    let mut boxes: Vec<Box<dyn MyOtherTrait>> = vec![A{}, A{}, A{}].into_iter().map(|a| Box::new(a) as Box<dyn MyOtherTrait>).collect();
    doit(&mut boxes);
    for item in boxes.iter_mut() {
        item.doit_too();
    }
}
error[E0277]: the trait bound `Box<dyn MyOtherTrait>: BorrowMut<(dyn MyTrait + 'static)>` is not satisfied
  --> src/main-dyn.rs:31:10
   |
31 |     doit(&mut boxes);
   |     ---- ^^^^^^^^^^ the trait `BorrowMut<(dyn MyTrait + 'static)>` is not implemented for `Box<dyn MyOtherTrait>`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `BorrowMut<dyn MyOtherTrait>` is implemented for `Box<dyn MyOtherTrait>`
note: required by a bound in `doit`
  --> src/main-dyn.rs:12:10
   |
11 | fn doit<T>(items: &mut [T])
   |    ---- required by a bound in this function
12 | where T: BorrowMut<dyn MyTrait>
   |          ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `doit`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `scratchpad` (bin "dyn") due to previous error

Where is the lifetime requirement 'static coming from? How can I process the vector of MyOtherTrait boxes as MyTrait?

For reference, I have tried to make a second vector Vec<&mut dyn MyTrait> from boxes, but that results in multiple mutable borrows when I call the function.


Solution

  • You can relax your requirements in doit to this:

    fn doit<T, U>(items: &mut [T])
    where
        T: AsMut<U>,
        U: MyTrait + ?Sized,
    {
        for item in items.iter_mut() {
            item.as_mut().doit();
        }
    }
    

    By not requiring it to give you dyn MyTrait directly, just anything that can produce a mutable reference to something that implements the trait, you can pass trait objects to things that implement a super trait as well.