Why does &mut dyn require 'static

I am playing around with a generational repository and stumbled upon the following error for this code:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
   = note: expected `&mut (dyn Element + 'static)`
              found `&mut (dyn Element + 'static)`

If you remove the iter_layer_elements_mut from the example, it compiles fine.

First of all, why does it need to be a 'static lifetime for the dyn Element for the return value of element_mut (line 26) but for the immutable version 'a is just fine.
Also, why does the first example not work and how do I get this to work.

This is an especially bad error message, I think there is already an issue tracking it.

The problem is that &mut _ means exclusive reference, and Rust can't prove that your iterator will never return overlapping references. This isn't a problem for &_ because that means shared reference.

The 'static is required because in ElementEntry you defined element: Box<dyn Element>, which is the same as element: Box<dyn Element + 'static>, and &mut T is invariant in T. The invariance means that lifetimes have to match exactly. &T is covariant in T, so it doesn't have to match exactly.


Thank you for your fast response!
I might finally get the idea of + 'static. But I still do not understand why it doesn't work.

With a slight modification - cloning the layers vec - the error remains the same. But shouldn't FnMut of flat_map be able to capture &mut self? And because FnMut requires also an exclusive reference for invocations, calls to it cannot overlap?

edit: old slight modification

Let's take a simpler example which shows the same kind of error:

fn with_mut_ref(array: &mut [i32]) {
    let array_copy = (0..array.len()).map(|i| &mut array[i]).collect::<Vec<_>>();

This can't be proven to be correct because Rust has no way of knowing that the indices i are all different. For example, (in rustc's eyes) there isn't a difference between the code above, and this code here:

fn with_mut_ref(array: &mut [i32]) {
    let array_copy = (0..array.len()).map(|i| &mut array[0]).collect::<Vec<_>>();

Which is obviously wrong because it has overlapping exclusive references for any slice with more than 1 element.

This is similar to how you are doing this:

.flat_map(|addr| self.elements.element_mut(addr))

There's nothing that proves that all calls self.elements.element_mut(addr) return a unique exclusive reference.

Note that with shared references, this problem doesn't matter.

fn with_ref(array: &[i32]) {
    let array_copy = (0..array.len()).map(|i| &array[i]).collect::<Vec<_>>();

I think I got it now. flat_map requires an FnMut that can be called multiple times, even if the previously returned value is not dropped yet and because I return an exclusive reference this is not true, because the FnMut ist still borrowed exclusively to the previous value.

Your simplified example is helping me a lot to understand the root cause of the issue. Thank you :slight_smile:

Is there an obvious/idiomatic way to work around this, for which I am just to silly to see?

You could iterate over all of the elements, and filter out the ones you don't want: Rust Playground

1 Like

I haven’t heard the use of invariance to explain the need to have matching lifetimes. I thought it was used to conclude whether one could be treated as a subtype, e.g., I can use T as a parameter in fn (input: &T) {}

Are you saying that invariance requires that “they” each have the same lifetimes because mut T is covariant with T?

That's not a contradiction. &mut T being invariant in T means that, when you have &mut T<'a> and &mut T<'b> with 'a != 'b, neither is a subtype of another, i.e. neither can be used where another one is expected.


Thank you. Just to be clear I wasn’t presuming the answer was incorrect, more my understanding of how invariance could be used to explain the situation. Your answer helped... if invariant, lifetimes must match. I still have more to figure out.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.