Is there some way to express that the lifetime in the parameter outlives the captured one?

// struct A<'a>{
//     r:&'a mut &'a i32
// }
// impl<'a> A<'a>{
//     fn call<'b:'a>(& mut self, v:&'b i32){
//         *self.r = v;
//     }
// }
trait MyFun<'a>{
    type Output;
    fn call(& mut self,v:&'a i32)->Self::Output;
}
impl<'a,F> MyFun<'a> for F
where F:FnMut(&'a i32){
    type Output = ();
    fn call(& mut self,v:&'a i32)->Self::Output{
        self(v);
    }
}
fn test<F>(f:F)
where F:for<'b> MyFun<'b>{
    
}
fn main(){
   let mut r = &1;
  test(|v|{  // #1
      r = v;
  });
//   let mut a = A{r:&mut r};
//   let ii = 0;
//   a.call(&ii);
}
use std::marker::PhantomData;

#1 causes a compiled error since the compiler does not know whether the lifetime in the parameter type outlives the captured one. I tried to use MyFun to workaround the issue, however, in the current rust, there is no way to express 'a:F, is there some way to achieve this purpose?

Incidentally, if write the lambda with

let f = |v|{r = v};

f itself is ok, however, this would make the lifetime of v a specific one.

The closure captures a, let's say, &'m mut &'r i32. Are you trying to get the compiler to generate something like this?

struct Closure<'m, 'r> {
    capture: &'m mut &'r i32,
}

impl<'m, 'r> Closure<'m, 'r> {
    fn call<'new: 'r>(&mut self, r: &'new i32) {
        *self.capture = r;
    }
}

(I'm not sure if there's a way to do that.)

There's at least one workaround: put something that implies a lifetime bound into the signature.

fn test<'r, F>(_f: F)
where
    F: for<'a> FnMut(&'a i32) -> [&'r &'a (); 0]
{}

fn main() {
    let local = 1;
    let mut r = &local;
    test(|v| {
        r = v;
        []
    });
}
Off topic I suppose

clicks on Error E0521

A type annotation of a closure parameter implies a new lifetime declaration. Consider to drop it, the compiler is reliably able to infer them.

:rofl: :rofl: :rofl:

If only, my dear error code author, if only.

I'm curious how is that 'r associated with the lifetime that would appear in the field capturing the variable r such that we prove 'a outlives 'r that outlives the captured one.

@quinedot Maybe another question, It is strange, in the following example, one is general enough and another is not

trait MyFun<'a>{}
impl<'a,F> MyFun<'a> for F
where F:FnMut(&'a i32) {

}
fn test<F>(f:F)  // #1
where F:for<'b> MyFun<'b>{
    
}
// #2
// fn test<F>(f:F) 
// where F:for<'b> FnMut(&'b i32){
    
// }
fn main(){
   test(|v|{});
}

#1 reports an error that

error: implementation of MyFun is not general enough

while #2 is ok. I didn't see any essential difference between them. Even though we go through Higher-ranked trait bounds - Rust Compiler Development Guide mentioned a similar case, iiuc, it should be ok

By having

fn test<'r, F> ... where F: for<'a> ...Implied{ 'a: 'r }...

the caller can choose a lower bound 'r for 'a.

My best guess as to what produces the error, and why you have to jump through hoops to get a working closure, is that it's a HRTB check -- where explicit bounds on lifetimes tend to prevent the check from succeeding.

Example
trait MyFnMut<Arg> {}

struct Closure<'m, 'r> {
    capture: &'m mut &'r i32,
}

// Lifetime involved in a bound on the implementation
//           vvvvvvvv
impl<'m, 'r, 'new: 'r> MyFnMut<&'new i32> for Closure<'m, 'r> {}

fn test<C: for<'new> MyFnMut<&'new i32>>() {}

fn main() {
    // Fails
    test::<Closure<'_, '_>>();
}
trait MyFnMut<Arg, Guard> {}

struct Closure<'m, 'r> {
    capture: &'m mut &'r i32,
}

// Bound is now implied               vvvvvvvvvvvv
impl<'m, 'r, 'new> MyFnMut<&'new i32, &'r &'new ()> for Closure<'m, 'r> {}

fn test<'r, C: for<'new> MyFnMut<&'new i32, &'r &'new ()>>() {}

fn main() {
    // Succeeds
    test::<Closure<'_, '_>>();
}

That's because Fn-trait bounds result in special treatment. When there's a Fn-trait bound on a function argument, the compiler will try to implement the closure in such a way that the bound is met, overriding the usual inference. This includes which Fn traits are implemented and also the arguments and return type of the closure.

Without the special treatment, the compiler is reliably able to infer an argument should carry a new lifetime declaration[1] the compiler is notoriously bad at realizing when arguments and the return type should have free or fixed lifetimes. If you add an annotation with an elided lifetime to an argument, it will infer what you wanted in this case.

So this works.

trait MyFun<'a> {}
impl<'a, F> MyFun<'a> for F where F: FnMut(&'a i32) {}

fn test<F>(_f: F)
where
    F: for<'b> MyFun<'b>,
{}

fn main() {
    //      vvvv
    test(|_v: &_| {});
}

  1. hey it was on topic afterall :slightly_smiling_face: ↩︎

Specifically, assuming the desugar to the closure type is something like:

struct Closure<'m, 'n> {
    capture: &'m mut &'n i32,  // #1
}
fn test<'r, F>(_f: F)
where
    F: for<'a> FnMut(&'a i32) -> [&'r &'a (); 0]
{}

What we directly want to prove to the compiler is that the lifetime 'a in the parameter as shown in the late bound of test outlives 'n at #1, however, just introduce the return type [&'r &'a (); 0], which implicitly impose the requirement 'a:'r will make the example ok, I just don't understand how is the 'r associated with 'n such that a reference &'a i32 can be assigned to &'n i32

In other words, the implied lifetime bound just says 'a:'r, but we need to say 'a:'n, I didn't see the relationship between 'r and 'n, so how does the compiler know 'a:'n?

Here's my first attempt at an answer:

At the call site, the compiler has to prove the test bound, which is

Exists<'r> such that ForAll<'a where 'a: 'r> F: FnMut(&'a i32) -> ...
//     ^^                      ^^^^^^^^^^^^
// Caller chooses              From the implied bound

So it just has to prove some lower bound 'r exists so that the HRTB is satisfied. It doesn't have to "choose" any particular lifetime, it just has to show a solution exists. That said, 'n is a lifetime that would work.[1]


I'm not sure if that actually answers your question though, or if it's accurate enough at this level of detail. Maybe the question could be, how does the compiler choose an implementation?

I don't know a complete answer to that, but can speculate. Here's a couple of nominal struct versions. They use 'n or 'static for 'r because those are the only lifetime names in scope which work.

My speculation is that the compiler can generate an analogous implementation for any lifetime within the required bounds instead -- including the unnameable, shorter-than-function-body lifetimes it infers.[2]

Another possibility is that 'r becomes another parameter of the closure type, but the required 'r: 'n needs to be a property of the type so that it is implied in the implementation (making the bound explicit in the implementation makes the HRTB fail). That seems a bit unnatural to me, but perhaps I'm wrong. Or the compiler may just have more tools than can be expressed with the language syntax.


  1. Until you add more code, so is 'static, or any other lifetime greater than 'n. But if you add code passing in a &'n i32 to the closure, it will still find the solution. ↩︎

  2. Something like, it creates a new lifetime 'r, that lifetime has an 'r: 'n bound due to the closure body, any time you call the closure with 'x you get a 'x: 'r bound too, and it otherwise just participates in borrow checking in the usual way. ↩︎

So, this aspect is more like a black box of the compiler. There are not too many rationales to directly interpret why adding the [&'r &'a ();0] can make the example compiled; merely adding that return type happens to make the compiler generate a usable implementation, right?

making the bound explicit in the implementation makes the HRTB fail

Most of the time, changing the explicit bound to an implicit one can make the HRTB check pass. How does it affect the "leak?" Is this because the explicit trait bound to lifetime will affect the taint set? For example:

trait Trait<'a>{}
impl<'a:'b,'b> Trait<'a> for &'b i32{}
fn foo<F>(_:F) where F: for<'a> Trait<'a>{}

fn main(){
    let i =0;
    foo(&i);
}

Saying replace the higher-ranked lifetime with placeholder '0, the taint set of '0 is {'0, '$a, 'b}, which makes "leak" check fail?

Sort of I suppose. My interpretations are based on how writable code works and whatever compiler docs or in-depth PRs and issues I've dug up. (I'm not aware of any spec-like RFC similar to the NLL RFC, say.) Writable code needs the [&'r &'a (); 0] too.

Hmm, let's see. We have to assume we got the type and implementation more or less right.

// The query
Closure<'m, 'n>: for<'a> FnMut(&'a i32) -> [&'r &'a (); 0]

// Replace bound parameters with placeholders
Closure<'m, 'n>: FnMut(&'0 i32) -> [&'r &'0 (); 0]

// Make another instance based in the impl using inference variables.
Closure<'m, 'n>: FnMut(&'$a i32) -> [&'r &'$a (); 0]

// Relate the trait refs
// { '0, '$a }

But if I do this without the [&'r &'a (); 0] I get the same answer.[1] I think the relevant part is actually the next section, impl obligations. If you have an explicit bound I believe you end up with something like

// explicit 'a: 'r bound on the implementation =>
for<'a> 'a: 'r

which is a bound that can't actually be met I believe. (The error you get is inaccurate.) (When dealing with upper bounds instead of lower bounds, you end up with a 'static requirement.)

Implied bounds don't generate these impl obligations.


  1. Hopefully I'm doing it right this time :slightly_smiling_face: ↩︎

To reduce some complex, discuss how the explicit lifetime trait bound in the implementation affects "leak", given this example:

trait Trait<'a>{}
impl<'a:'b,'b> Trait<'a> for &'b i32{}
fn foo<F>(_:F) where F: for<'a> Trait<'a>{}

fn main(){
    let i =0;
    foo(&i);
}

According to Higher-ranked trait bounds - Rust Compiler Development Guide, we have the following steps:

// The query
&'b i32: for<'a> Trait<'a>

// Replace bound parameters with placeholders.
&'b i32:  Trait<'0>

// Match the impl against the placeholder obligation.
/* impl<'$a:'b>*/ Trait<'$a> for &'b i32{}

'0 == '$a

The taint set for '0 is at least {'0, $'a}, I don't know whether the set should include 'b, the specification does not mention this part, however, if the taint set for '0 is solely {'0, '$a}, the check should succeed, however, the compiler says the implementation is not general, so, I wonder is that 'b in the explicit trait bound considered as an element in taint set for '0 such that the check is considered to fail? Otherwise, we cannot interpret the reason.

I'm saying that the impl obligation parts goes like so:

Taint set is { '0, $'a }
Trait obligation after matching is '0: 'b (from `impl<'a: 'b, 'b>`)
Convert every lifetime in the taint set back into a HR bound:
Trait obligation is for<'a> 'a: 'b

And that the last part is the problematic part that causes the original obligation to fail.

But, why does Trait obligation is for<'a> 'a: 'b make the original obligation fail? I meant I didn't find the documented rationale in placeholder leak. Or, from the perspective of the informal interpretation, the higher-ranked lifetime lifetime 'a denote any lifetime, we cannot find that 'b that is outlived by any lifetime?

If it is because there is no that 'b such that for<'a> 'a:'b, which causes the original obligation to fail, why does the implicit one(formed from type) not make the obligation fail?

Some things are considered inputs to the trait solving; things it can take for granted. From what I understand, implied bounds from well-formedness is one of those. Some other part of the compiler is responsible for ensuring those hold (and if something slips through, that's a soundness hole).

Beyond that -- for things like "why is it this way" or other rationale -- I'm afraid I don't have any answer. Pruning explicit bounds that are the same as implicit bounds in particular seems like low hanging fruit to me, but we don't have it.

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.