Why the baz1 does not require 'g: 'f, but baz2 does?

Someone told me checking the bound can be deffered, but I don't know why baz1 can be deffered, but baz2 can't

    struct Foo<'a, 'b: 'a>(&'a mut &'a (), &'b ());

    fn baz1<'f, 'g>(b: &'f i32, _foo: Foo<'f, 'g>) {}
    pub fn baz2<'f, 'g: 'f, F>(f: F) -> ()
    where
        F: FnOnce(&'f Foo<'f, 'g>) -> (),
    {
        ()
    }

Because your're holding a reference &'f of Foo<'f, 'g>, so 'g must outlive the lifetime of that reference.
Also read: &'a Struct<'a> and covariance - Learning Rust

Well,

 fn baz1<'f, 'g>(b: &'f i32, _foo: &'f Foo<'f, 'g>) {}

still can be accepted, and does not require explicit 'g: 'f

The difference is that the things which imply 'g: 'f are inputs to baz1 but in a where clause for baz2.

If the question is "but why is it like that", I don't really have an answer.

I am not an expert, but as I see it, in:

fn baz1<'f, 'g>(b: &'f i32, _foo: &'f Foo<'f, 'g>) {}

the &'f Foo<'f, 'g> reference is an input, so the caller can verify (by being able to construct that reference) that 'g: 'f, however in:

pub fn baz2<'f, 'g: 'f, F>(f: F) -> ()
where
    F: FnOnce(&'f Foo<'f, 'g>) -> ()

the &'f Foo<'f, 'g> reference is something like an output (an input to an input), the implementation of baz2 will (probably) construct such reference (so it can call f) and to do so it will need 'g: 'f.

As for why such bound is not implied, I do not know, but my guess would be to keep open the possibility to make references always valid but sometimes uninhabited types (e.g. &'long &'short T would be a valid type with no values).

Then the baz2 without the 'g: 'f bound would be valid and it would essentially say that baz2 does not call f.

So if you look at this header:

pub fn baz2<'f, 'g, F>(f: F) -> ()
where
    F: FnOnce(&'f Foo<'f, 'g>) -> ()

it might have two meanings:

  1. You do want to call f in baz2. But then you need also 'g: 'f,
  2. You do not want to call f in baz2. But then the F: FnOnce(&'f Foo<'f, 'g>) is redundant.
  3. You might also want to do something that does not call f, but still needs the F: FnOnce(&'f Foo<'f, 'g>) bound[1], but that is invalid in current rust.

The Rust authors probably did not want to make any of these implicit. You can choose explicitly by adding 'g: 'f or omitting F: FnOnce(&'f Foo<'f, 'g>).

There is no such ambiguity in baz1 (the latter version that takes _foo by reference), without the 'g: 'f it is uncallable.

But that is just my guess.


  1. hard to imagine with this particular bound and other arguments, but possible in general ↩︎