T<'a> is late bound?

#![allow(unused_variables)]

struct B<'a> {
    a: &'a i32,
}

fn test1<'a>(b: B<'a>) -> i32 {
    0
}

fn main() {
    let n: i32 = test1;
}

tells test1 is late bound on 'a:

12 |     let n: i32 = test1;
   |            ---   ^^^^^ expected `i32`, found fn item
   |            |
   |            expected due to this
   |
   = note: expected type `i32`
           found fn item `for<'a> fn(B<'a>) -> i32 {test1}`

Then b:B<'a> should be late bound;
While, B<'a> is early bound by definition;

How to understand the conflict?

Is B<'a> a different type vs B<'b> given 'a != 'b?
If the above is true, then fn(B<'a>) -> i32 is a different type vs fn(B<'b>) -> i32 given 'a != 'b?

Then it is conflict with for<'a> fn(B<'a>) -> i32 {test1} to be a single type.

'a is late bound for test1 as it's not in any explicit bounds, yes.

Here's an alternative where it's early bound:

fn test2<'a: 'a>(b: B<'a>) -> i32 { 1 }

There's no conflict. They have notional definitions and trait implementations like so.

struct fn#test1;
impl<'a> Fn(B<'a>) -> i32 for fn#test1 { ... }

struct fn#test2<'a>(PhantomData<fn(B<'a>) -> i32>);
impl<'a> Fn(B<'a>) -> i32 for fn#test2<'a> { ... }

Yes.

Function item types may or may not have lifetime parameters (i.e. early bound lifetimes); if they have the parameter, different lifetimes result in different types. If they don't, it's not parameterized by that lifetime; the lifetime is just part of it's Fn-trait implementations.

Try to turbofish test1 and you'll see it's not allowed (it doesn't have a lifetime parameter).

let m = test1::<'static>;

I understand that, but arguments may be not part of Function item types either?

If B must be part of the Function item types, then lifetime must be part of it too, because B is not complete without a lifetime.

The function item types in question have no B<'a> field or anything like that. There's no reason they need it.

Consider this implementation:

impl From<&str> for String {
    fn from(s: &str) -> Self {
        s.to_string()
    }
}

// With less elision
impl<'a> From<&'a str> for String {
    fn from(s: &'a str) -> Self {
        s.to_string()
    }
}

String can have these implementations without having a &str field or a lifetime parameter, no problem. It's the same idea with function item types and late-bound parameters, and their implementations of the Fn traits.

Or this...

1 Like

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.