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.

1
  • "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. Commented Aug 18, 2020 at 16:08

2 Answers 2

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:

Sign up to request clarification or add additional context in comments.

5 Comments

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
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>.
@SvenMarnach IndexMut is indeed implemented for Vec<_> (or maybe slice). I think this is your nemesis, reborrowing. 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.
@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.
It turned out that the code in the original post is working on beta and nightly, so it looks like this answer is wrong.
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].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.