Skip to content

Higher rank lifetimes fails when trying to set closure lifetimes #116869

Closed as not planned
@GregoryConrad

Description

@GregoryConrad

I'm having a hard time minimizing this code sample, so sorry in advance.

The real issue I'm facing is #111662, so please see my comment for some context.
However, since that issue isn't fixed, I needed a workaround.
Here's the basic jist of my workaround:

fn main() {
    // ERROR: 
    let _c = |b: S<'_>| -> &() { b.thing() }; //~ ERROR lifetime may not live long enough
    // WORKAROUND:
    let _c = fix_lifetime(|b: S| b.thing());
}

// Let's help the compiler out a little:
fn fix_lifetime<F, T>(f: F) -> F
where
    F: for<'a> FnOnce(S<'a>) -> &'a T,
{
    f
}

struct S<'a>(&'a ());
impl<'a> S<'a> {
    fn thing(self) -> &'a () {
        self.0
    }
}

This workaround works for the provided example and I'm guessing 99% of other situations.

But, my actual issue is a bit more complicated so I get a:

error: higher-ranked lifetime error
  --> rearch/src/side_effects.rs:13:5
   |
13 |     fix_lifetime(move |register: SideEffectRegistrar| register.raw(initial))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

No further information is provided.

Full copy of the problematic code
// This trait is the main problem. It is equivalent to FnOnce(SideEffectRegistrar) -> T
pub trait SideEffect {
    type Api<'a>;
    fn build<'a>(self, registrar: SideEffectRegistrar<'a>) -> Self::Api<'a>;
}
impl<T, F: FnOnce(SideEffectRegistrar) -> T> SideEffect for F {
    type Api<'a> = T;
    fn build<'a>(self, registrar: SideEffectRegistrar<'a>) -> Self::Api<'a> {
        self(registrar)
    }
}

pub fn raw<T: Send + 'static>(
    initial: T,
) -> impl for<'a> SideEffect<
    Api<'a> = (
        &'a mut T,
        impl Fn(Box<dyn FnOnce(&mut T)>) + Clone + Send + Sync,
    ),
> {
    fix_lifetime(move |register: SideEffectRegistrar| register.raw(initial)) // ------------------- ERROR HERE
}

// Workaround from above to try to fix the underlying issue.
fn fix_lifetime<F, T, R>(f: F) -> F
where
    F: for<'a> FnOnce(SideEffectRegistrar<'a>) -> (&'a mut T, R),
{
    f
}


// Not sure if this is needed, but here it is just in case:
pub struct SideEffectRegistrar<'a> {
    side_effect: &'a mut OnceCell<Box<dyn Any + Send>>,
    rebuilder: Box<dyn SideEffectRebuilder>,
}
impl<'a> SideEffectRegistrar<'a> {
    // Other methods left out for brevity.
    pub(crate) fn raw<T>(
        self,
        initial: T,
    ) -> (
        &'a mut T,
        impl Fn(Box<dyn FnOnce(&mut T)>) + Clone + Send + Sync + 'static,
    )
    where
        T: Send + 'static,
    {
        self.side_effect.get_or_init(|| Box::new(initial));
        let data = self
            .side_effect
            .get_mut()
            .expect(PREVIOUS_INIT_FAILED_MSG)
            .downcast_mut::<T>()
            .unwrap_or_else(|| panic!("{}", EFFECT_FAILED_CAST_MSG));
        let rebuild = move |mutation: Box<dyn FnOnce(&mut T)>| {
            (self.rebuilder)(Box::new(|data| {
                let data = data
                    .downcast_mut::<T>()
                    .unwrap_or_else(|| panic!("{}", EFFECT_FAILED_CAST_MSG));
                mutation(data);
            }));
        };
        (data, rebuild)
    }
}

Meta

Tested on nightly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-closuresArea: Closures (`|…| { … }`)A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions