Lifetime issue I can't even put into words

I don't know what words to use to describe this issue so I'm going to post the absolute minimal example in the rust playground and hope the error message can guide someone more knowledgeable to a solution.

So I'm basically trying to implement Filter for FiltersOnDimension so that I can wire the output of Dimension get fn, inside FiltersOnDimension, to the input of the Fil param's filter() fn. Now if I remove the lifetime on type In<'a> in the Filter trait, the problem goes away. But then I can't have the option to specify a reference as the type of type In in the future. Specifically for Equals type. Notice how the impl on Equals specifies a type In<'a> = &'a T. This is nice. Sometimes I would like to type In = Type and sometimes type In = &Type. The problem is getting the Filter trait as it is to behave with the impl on FilterOnDimension now..

I don't understand what your code is doing, but I tried to fix some things (just by following the compiler errors). This is as far as I got: Playground. But now I can't get further because the trait doesn't have the other lifetime 'b, so I can't restrict the lifetime argument 'a on the implementation of filter.

But as I said, I'm not really overlooking your code. I just followed the compiler errors.

Line 41 is what I'm trying to eventually work towards. Once all this works I can do this

    let row = (1u32, 1u32, String::new());
    let f1 = Equals(1u32);
    let d1 = Dimension(Id, PhantomData::default(), PhantomData::default());
    let fd = FiltersOnDimension(d1, f1, PhantomData::default());
    fd.filter(row);

It's important that Equals, Dimension and FiltersOnDimension all impl Filter.

I was able to get it to compile by changing
fn filter<'a>(&self, _: Self::In<'a>) -> bool; to
fn filter<'a>(&'a self, _: Self::In<'a>) -> bool;

and then changing
type In<'a> = IdxIn to
type In<'a> = IdxIn where Self: 'a;

But now that it compiles, it's time to uncomment line 41 and.. boom. Cascade of new lifetime issues. Rust Playground

I still don't understand the example. (So don't take my example for a solution.) But I tried to use a higher-ranked trait-bound, and ran into a compiler-flaw, I think:

 impl<'b, IdxOut, Idx, Fil> Filter for FiltersOnDimension<'b, Id, IdxOut, Idx, Fil>
     where
         Idx: Indexed<Id, IdxOut>,
-        Fil: Filter<In<'b> = IdxOut>,
+        for<'c> Fil: Filter<In<'c> = IdxOut>,
 {

(Playground)

   Compiling playground v0.0.1 (/playground)
error[E0311]: the parameter type `Fil` may not live long enough
  --> src/lib.rs:33:28
   |
33 | impl<'b, IdxOut, Idx, Fil> Filter for FiltersOnDimension<'b, Id, IdxOut, Idx, Fil>
   |                       ---  ^^^^^^ ...so that the type `Fil` will meet its required lifetime bounds
   |                       |
   |                       help: consider adding an explicit lifetime bound...: `Fil: 'a`

error[E0311]: the parameter type `Fil` may not live long enough
  --> src/lib.rs:40:8
   |
33 | impl<'b, IdxOut, Idx, Fil> Filter for FiltersOnDimension<'b, Id, IdxOut, Idx, Fil>
   |                       --- help: consider adding an explicit lifetime bound...: `Fil: 'a`
...
40 |     fn filter<'a>(&'a self, input: Self::In<'a>) -> bool {
   |        ^^^^^^ ...so that the type `Fil` will meet its required lifetime bounds

error: could not compile `playground` due to 2 previous errors

The compiler suggests to add a bound Fil: 'a, where 'a isn't defined.

Could this be related to GATs being incomplete?

All of this can work if I just give up the need for optionally specifying a reference type on the associated type.

trait Filter {
    type In;
    fn filter(&self, _: &Self::In) -> bool;
}

But it sucks that I have to give that up now that we have GATs.

The flawed help message in my example: yes.

But don't trust my examples to be the right solution for you as I really didn't understand what you try to do in your code. Maybe someone else can help better looking at your original code. Or perhaps you can explain step-by-step what you are trying to achieve if that's not too much work? The tuple structs (i.e. structs without field names but .0, .1 etc. make it even more difficult (for me) to follow. (But I generally struggle with following Rust code not written by myself. I lack experience yet.)

I believe this is similar to @jbe's first attempt, but I propagated the new where Self: 'a bound on the method to both implementations. It compiles -- I had to adjust the previously commented out line a bit to accommodate the nested tuple structs. I don't understand the overarching goal either, but hopefully this helps.

1 Like

That works! In addition to your solution, I've been trying to replace the need for a dummy lifetime param on FilterOnDimension<'b.. with a higher ranked bound but with no luck. Anyway to do that?

Like so?

I did have to add where Fil: 'static to the impl in order to quiet some errors. Basically I think it wants Fil to be valid wherever Self is valid and thus wherever the GAT is valid. However, this must be true for WF and thus no bound should be required. Also if I'm right, the 'static bound is more than is required, but it would take some type gymnastics and dummy field scaffolding to create a tighter bound.

But I might be wrong in my interpretation. I haven't taken the time to search for a GAT bug about this.

1 Like

Yes, this is a limitation I had encountered elsewhere (maybe over Zulip) where the stable polyfill / workaround (with the implicit/implied bounds) does not need that 'static bound, and yet the official/straightforward GAT implementation does.

Hence the following solution, which takes yours and replaces the (L)GAT with the stable polyfill, hence getting rid of the need for that : 'static bound.

2 Likes

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.