0

I need to iterate over a vector of mutable references; here is a simplified reproduction:

trait Ticking {
    fn tick(&mut self);
}

trait Fish {}

struct World<'a> {
    fish: Vec<&'a mut dyn Fish>,
}

impl<'a> Ticking for World<'a> {
    fn tick(&mut self) {
        let _fish: &mut dyn Fish = self.fish[0];
        //let _fish: &mut dyn Fish = self.fish.get_mut(0).expect("expected value");
    }
}

struct Guppy<'a> {
    n_ref: &'a usize,
}

impl<'a> Fish for Guppy<'a> {}

fn main() {
    let mut guppy: Guppy = Guppy { n_ref: &5 };
    let _world: World = World {
        fish: vec![&mut guppy],
    };
}

I received the following error:

error[E0596]: cannot borrow data in an index of `std::vec::Vec<&mut dyn Fish>` as mutable
  --> src/main.rs:15:36
   |
15 |         let _fish: &mut dyn Fish = self.fish[0];
   |                                    ^^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::vec::Vec<&mut dyn Fish>`

I attempted to call get_mut directly and received a lifetime bound error:

error[E0277]: the trait bound `&'a mut (dyn Fish + 'a): Fish` is not satisfied
  --> src/main.rs:13:36
   |
13 |         let _fish: &mut dyn Fish = self.fish.get_mut(0).expect("expected value");
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Fish` is not implemented for `&'a mut (dyn Fish + 'a)`
   |
   = note: required for the cast to the object type `dyn Fish`

The compiler explanations were unhelpful in determine the root cause here.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
joshmeranda
  • 3,001
  • 2
  • 10
  • 24
  • "received a lifetime bound error" - This is not actually a lifetime bound error, but a trait bound error. The compiler tries to assign the RHS type `&mut &mut dyn Fish` to the LHS type `&mut dyn Fish`. That would be possible **if** `&mut dyn Fish` implemented `Fish`, but it doesn't. – Sebastian Redl Aug 18 '20 at 16:08

2 Answers2

1

You are (1) using the wrong syntax for indexing and (2) your type mismatches:

let _fish: &mut &mut dyn Fish = &mut self.fish[0];
//         ^^^^ 2               ^^^^ 1

There's no reason to have an explicit type here anyway:

let _fish = &mut self.fish[0];

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Ah silly mistake on my part, thanks for the response! I agree there is no real reason that this needs an explicit type, I tend to prefer explicit typing over implicit typing wherever – joshmeranda Aug 18 '20 at 13:09
  • Hmm, I may be missing something obvious here but I don't understand why this is required, nor do I understand the error message. As far as I can tell, `IndexMut` _is_ implemented for `std::vec::Vec<&mut dyn Fish>`. – Sven Marnach Aug 18 '20 at 15:58
  • @SvenMarnach `IndexMut` is indeed implemented for `Vec<_>` (or maybe slice). I think this is [your nemesis, reborrowing](https://stackoverflow.com/questions/61195410/why-cant-i-assign-one-dereference-of-a-reference-of-a-reference-to-another-when#comment108260319_61196186). `self.fish[0]` (without preceding `&mut`) would mean to move the value out of the `Vec` / `slice`. That's not allowed, so the compiler attempts to reborrow. However, it's [already decided to use `Index` instead of `IndexMut`](https://stackoverflow.com/a/52990677/155423). – Shepmaster Aug 18 '20 at 16:05
  • @Shepmaster I don't think implicit reborrowing is relevant here. The last point does seem to be what's happening though, and the error message is plain wrong. For what it's worth, reborrowing does become relevant when slightly changing the code: `let _fish: &mut _ = *&mut self.fish[0];` works fine, while `let _fish = *&mut self.fish[0];` doesn't. – Sven Marnach Aug 18 '20 at 17:53
  • It turned out that the code in the original post is working on beta and nightly, so it looks like this answer is wrong. – Sven Marnach Aug 19 '20 at 09:27
1

The compiler incorrectly chooses the Index trait over the IndexMut trait here, and gives a factually wrong error message. I filed a bug for this behaviour, but it turns out that this is actually fixed in the beta and nightly versions of Rust. Beta will be released as stable next week, so in the future your code will just work.

In the meantime, there are several ways of making the code work on the current stable version and older versions of Rust. The most succinct way is to force the compiler to choose IndexMut by adding &mut only on the right-hand side of the assignment:

let _fish: &mut dyn Fish = &mut self.fish[0];

The right-hand side has type &mut &mut dyn Fish now, so a deref coercion will be applied by the compiler. Alternatively, you can explicitly dereference the right-hand side, *&mut self.fish[0].

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841