Compiler unable to infer lifetime when calling RefCell::borrow()

Hi,
I have some code that roughly looks like this.

trait Store<'storage, T> {
    type Iter;
    type IterMut;

    fn iter(&'storage self) -> Self::Iter;
    fn iter_mut(&'storage mut self) -> Self::IterMut;
 }

 trait Storable {
     type Store;
 }

struct VecStore<T> {
    data: Vec<T>
}

impl<T> VecStore<T> {
    fn new() -> Self {
        VecStore { data: Vec::<T>::new() }
    }
}

impl<'storage, T> Store<'storage, T> for VecStore<T> where T: 'static {
    type Iter = std::slice::Iter<'storage, T>;
    type IterMut = std::slice::IterMut<'storage, T>;

    fn iter(&'storage self) -> Self::Iter {
        self.data.iter()
    }

    fn iter_mut(&'storage mut self) -> Self::IterMut {
        self.data.iter_mut()
    }
}

struct Dispatcher<'dispatcher, T> where
    T: Storable {
    receiver: fn(&mut <<T as Storable>::Store as Store<T>>::Iter),
    store: Rc<RefCell<T::Store>>,
    phantom: PhantomData<&'dispatcher T>,
}

impl<'dispatcher, T> Dispatcher<'dispatcher, T> where
    T: Storable,
    T::Store: Store<'dispatcher, T>,{
    pub fn dispatch(&self) {
        (self.receiver)(&mut self.store.borrow().iter());
    }
}

When compiling the following error is reported on the store.borrow() call:
Cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
expected Store<'_, T>
found Store<'dispatcher, T>

If I understand it correctly I get this because I use 'dispatcher in the bounds for T::Store, while there is an anonymous lifetime used by the function pointer type. I tried using HRTB as shown in rust - Associated type lifetime when passing internal reference to a generic closure - Stack Overflow but I could not get this to work.

My understanding of how to properly use lifetimes is clearly limited, I would appreciate some help trying to understand what the proper solution to this problem is :slight_smile:

Many thanks,
Michael

The short answer is you’d need something like

impl<T> Dispatcher<T>
where
    T: Storable,
    T::Store: for<'storage> Store<'storage, T>,
{
    pub fn dispatch(&self) {
        (self.receiver)(&mut self.store.borrow().iter());
    }
}

(and I’ve removed the lifetime parameter of the Dispatcher.

However this won’t work too well when T: 'static is violated. Well it won’t work with your VecStorage anyways in this case, but your implementation can be relaxed from

impl<'storage, T> Store<'storage, T> for VecStore<T>
where
    T: 'static,
{
    // ...
}

to

impl<'storage, T> Store<'storage, T> for VecStore<T>
where
    T: 'storage,
{
    // ...
}

and then you might run into use-cases that don’t work. For example

impl<T> Storable for T {
    type Store = VecStore<T>;
}

fn test<'a>(d: Dispatcher<&'a u8>) {
    d.dispatch();
}
error: implementation of `Store` is not general enough
  --> src/lib.rs:68:7
   |
68 |     d.dispatch();
   |       ^^^^^^^^ implementation of `Store` is not general enough
   |
   = note: `VecStore<&'a u8>` must implement `Store<'0, &'a u8>`, for any lifetime `'0`...
   = note: ...but it actually implements `Store<'1, &'a u8>`, for some specific lifetime `'1`

(playground)

What you really want is some kind of way of saying

T::Store: for<'storage /* but only for those lifetimes such that T: 'storage */> Store<'storage, T>

This can best be achieved by implementing the Store-related traits on the reference types themselves instead of the values. This would mean splitting things up, e.g. like this:

trait RefToStore<T> {
    type Iter;
    fn iter(self) -> Self::Iter;
}

trait MutRefToStore<T> {
    type IterMut;
    fn iter_mut(self) -> Self::IterMut;
}

trait Store<T>
where
    for<'storage> &'storage Self: RefToStore<T>,
    for<'storage> &'storage mut Self: MutRefToStore<T>,
{
}

// etc…
// receiver: fn(&mut <&<T as Storable>::Store as RefToStore<T>>::Iter),

problem: the where clauses on the Store trait don’t act like supertraits, instead you’ll have to repeat them everywhere

impl<T> Dispatcher<T>
where
    T: Storable,
    T::Store: Store<T>,
    // vvvvvvvvvvvvvvvvvvvv this part isn’t too nice
    for<'storage> &'storage T::Store: RefToStore<T>,
    for<'storage> &'storage mut T::Store: MutRefToStore<T>,
{
    pub fn dispatch(&self) {
        (self.receiver)(&mut self.store.borrow().iter());
    }
}

impl<T> Storable for T {
    type Store = VecStore<T>;
}

fn test<'a>(d: Dispatcher<&'a u8>) {
    d.dispatch();
}

(playground)

The Store trait doesn’t actually really do anything anymore here, so it might be reasonable to remove it. You can also consider using existing trait infrastructure around IntoIterator like this.

All of this will probably become a lot more easy once we have GATs (generic associated types); in any case you might’ve noticed that this is getting pretty advanced and lifetime-heavy, so if there’s any way the need for all these traits can be avoided, that might be the best route, I guess.

3 Likes

Thanks for taking the time and providing me with such a detailed reply! In my particular case I believe I can get away with T being bound by a 'static lifetime.

Thanks again,
Michael