Tricky lifetimes: expected mutable reference `&'a mut _`found mutable reference `&mut _`

I'm new to Rust and trying to write my little ECS (for educational purposes mostly). I'm stuck for almost a week with this part - to implement generic visitor for a single component (it's a start, right?) for a "system" callback of the form of fn(comp_ref){}.
Unfortunately, this mix of generics, refs and lifetimes is clearly too much for me to grasp:

Here visitor is accepting a slice to column data and should iterate over it calling provided handler for each item in slice. Problem is I'm getting compilation errors like:
62 | let vis: Visitor1<&mut i32, > = Visitor1::new(|: &mut i32| {});
| ^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected mutable reference &'a mut _
found mutable reference &mut _
and I don;t understand how fix them.

Is it possible to achieve my goal?

impl<T> Accessor for &mut T
where
    T: 'static,
{
    type Slice<'a> = &'a mut [T];
    type ItemRef<'a> = &'a mut T;

This is an odd pattern. If we expand the elided lifetimes a bit we get:

impl<'t, T> Accessor for &'t mut T
where
    T: 'static,
{
    type Slice<'a> = &'a mut [T];
    type ItemRef<'a> = &'a mut T;

Note that &'t mut T and &'a mut T are different types, unless 'a == 't.

Then later you have

impl<A, H> Visitor1<A, H>
where
    H: Fn(A),
    for<'a> A: Accessor<ItemRef<'a> = A> + 'a,

Generic type parameters like A must resolve to a single type. And there is no for<'a> &'a mut A type. If we rewrite this to match the only implementation the best we can, it would be

impl<'t, T, H> Visitor1<&'t mut T, H>
where
    H: Fn(&'t mut T),
    for<'a> &'t mut T: Accessor<ItemRef<'a> = &'t mut T> + 't,
    //                          ^^^^^^^^^^^^^^^^^^^^^^^

I said "the best we can" because your implementation cannot actually meet the bound. ItemRef<'a> isn't &'t mut T in the implementation, it's &'a mut T.


I don't see any particular reason for the implementing type to be &mut T, so I would suggest instead trying

impl<T> Accessor for T
where
    T: 'static,
{
    type Slice<'a> = &'a mut [T];
    type ItemRef<'a> = &'a mut T;

And the bounds for Vistor1 become

impl<A, H> Visitor1<A, H>
where
    H: Fn(&mut A),
    A: 'static,
    for<'a> A: Accessor<ItemRef<'a> = &'a mut A>,

(H now has to accept any lifetime in the parameter, not just one lifetime.)

1 Like

Yes, but it's because there shall be another impl Accessor for &T

And more visitors, like Visitor2<A,B,H>, Visitor3<A,B,C,H>, etc. Where each A,B,C could be either shared or exclusive reference to some type. So that user could pass system function like H: Fn(&mut Location, &Velocity, &Direction).

It looks like &'a mut Self::Slice<'_> in the signature of get_at should really just be Self::Slice<'a>; right now it results in &mut &mut for no obvious reason.

Regarding the issue that @quinedot mentioned, it seems you are using &mut T as a "marker type". If you really can't avoid using a marker type, you could define one explicitly, e.g. struct MutAccessor<T>(PhantomData<T>). Not sure how idiomatic that would be, but I'd consider it as a last resort.

1 Like

Since there's no use of Self directly, you can use a marker type for the implementations. Say Ref<T> and RefMut<T>.

Then also, if you consider those canonical implementations of the traits, your Visitor implememtations always fully constrain the GAT, you may be able to just use Ref<T> and RefMut<T> instead of the trait bound.

If that all applies, you may also be implementing a "concrete abstraction" (with unneeded generic scaffolding).