rust

Lifetime tagging attempt fails with "lifetime does not live long enough"


I'm attempting to implement lifetime tagging like that demonstrated in this playground but I've gone astray somewhere and can't figure out what I did wrong.

Here is the error I'm getting. It only appears if the v.pup(...) line is there.

error[E0597]: `arena` does not live long enough
  --> src/lib.rs:65:5
   |
64 |       let arena = goop::Parent;
   |           ----- binding `arena` declared here
65 |       arena.scope(|child, context| {
   |       -^^^^
   |       |
   |  _____borrowed value does not live long enough
   | |
66 | |         let mut v = Foo::new(child);
67 | |         v.pup(context);
68 | |     });
   | |______- argument requires that `arena` is borrowed for `'static`
69 |   }
   |   - `arena` dropped here while still borrowed

Here is my code, it's as minimal as I could make it:

#![no_std]

mod goop {
    use core::marker::PhantomData;

    // Cell<T> is invariant in T; so Cell<&'id _> makes `id` invariant.
    // This means that the inference engine is not allowed to shrink or
    // grow 'id to solve the borrow system. 
    type Id<'id> = PhantomData<::core::cell::Cell<&'id mut ()>>;
    
    #[derive(Clone, Copy)]
    pub struct Child<'id> {
        _id: Id<'id>,
    }
    
    pub struct Parent;
    
    #[derive(Clone, Copy)]
    pub struct Context<'id, 'p> {
        _id: Id<'id>,
        parent: &'p Parent,
    }
    
    impl Parent {
        pub fn scope<'p, F>(&'p self, _f: F)
        where
            F: for<'id> FnOnce(Child<'id>, Context<'id, 'p>)
        {}
        
        fn internal_something(&self) {}
    }
    
    pub trait Gator<C> {
        fn gate(&self, c: C);
    }
    
    impl<'p, 'id> Gator<Context<'p, 'id>> for Child<'id> {
        fn gate(&self, c: Context<'p, 'id>) {
            c.parent.internal_something();
        }
    }
}

use core::marker::PhantomData;

struct Foo<C, G> {
    _c: PhantomData<C>,
    g: G,
}
impl<C, G: goop::Gator<C>> Foo<C, G> {
    fn new(g: G) -> Self {
        Self {
            _c: PhantomData,
            g,
        }
    }
    
    fn pup(&mut self, c: C) {
        self.g.gate(c)
    }
}

fn _check() {
    let arena = goop::Parent;
    arena.scope(|child, context| {
        let mut v = Foo::new(child);
        v.pup(context);
    });
}

playground


Solution

  • In this impl you swapped the lifetimes of Context:

    impl<'p, 'id> Gator<Context<'p, 'id>> for Child<'id> {
        fn gate(&self, c: Context<'p, 'id>) {
            c.parent.internal_something();
        }
    }
    

    This error in combination with the v.pup(context) call causes the compiler to deduce that 'p and 'id must be exactly the same lifetime. Due to the rest of the constraints in the program, the only way this can be satisfied is if both are 'static, so the compiler assumes this. That assumption then causes the error you see.

    This is a case of reductio ad absurdum but where you only get to see the final conflicting step, not the chain of logic that led up to it.

    Swap them around and the code compiles:

    impl<'p, 'id> Gator<Context<'id, 'p>> for Child<'id> {
        fn gate(&self, c: Context<'id, 'p>) {
            c.parent.internal_something();
        }
    }
    

    (Playground)