'a: T bound

Here's a crazy idea! Could we support 'a: T with the semantics that 'a must live at least as long as all the lifetime parameters in T? Currently, this can be simulated with traits:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b6579aa577c0b679d28c50b51da5ffdc

pub trait MaxLifetime<'a> {}

The rules for implementing MaxLifetime<'a> for a given type T:

All the fields of T should implement MaxLifetime<'a>. All types with no type or lifetime parameters implement for<'a> MaxLifetime<'a>, and references MaxLifetime<'a> like so

impl<'a: 'b, 'b, T: ?Sized> MaxLifetime<'a> for &'b T {}

These rules could be encoded via a derive macro, however, this feels like something that the language should handle.

Spawned from this URLO question

12 Likes

The syntax 'a: T seems much too clever and would add to lifetime confusion by Rust learners. I don't mean to doubt that this would be a useful feature, but an uncommonly-used feature like this should have a verbose name that gives people who haven't seen it before a clearer hint about the meaning.

5 Likes

I agree that any new extension to lifetimes should be met with scrutiny, in large part because beginners get tripped up with lifetimes. However since the syntax and semantics are so similar to existing constructs, I don't think this is too much of an additional burden. Said another way, if you understand T: 'a, then 'a: T is not that big of a step because they are analogous in syntax and semantics.

I agree that this is a niche use-case, but it has come up more than a few times in my code and in others' as well.

7 Likes

What would be the semantics of this when T doesn't contain any lifetimes? And error or would it be the same as 'a: 'static?

This rules specifies it

All types with no type or lifetime parameters implement for<'a> MaxLifetime<'a>

1 Like

This means struct A(&'static str); couldn't be used where struct A(String); could, which doesn't seem right...

This is because you could have code:

fn f<'a, T>(v: T, r: &'a u8) where 'a: T {
}

struct A<T>(T);

fn g() {
    let v: u8 = 0;
    f::<A<&'static str>>(A(""), &v); // fails because v doesn't live as long as 'static
    f::<A<String>>(A(String::new()), &v); // succeeds because String has no lifetimes
}
1 Like

A solution could be to have the compiler special-case explicit 'static bounds to mean that references should be treated like they has no lifetimes

Me - still a relative newbie - reads this as requiring &v to be 'static. Which it is not, so this should fail.

Have I misread the proposal? If I have is this a way to enhance it?

I dispute this, because it took me tens of minutes to understand what this feature means, and now that I do understand what it means, it seems like it is not what I would expect from the syntax 'a: T. In particular, T: 'a means that T's lifetime includes all of 'a, so I would expect that 'a: T would mean that 'a includes all of T's lifetime (so, for example, 'a: i32 would imply that 'a is 'static).

I also haven't been able to grok what the URLO thread is trying to do that requires this, so it's hard for me to come up with an alternative syntax that I would prefer (since that would require me to understand the purpose of the feature).

5 Likes

Exactly my reading. Is this not what @RustyYato suggested?

After thinking about this some more, I have to agree with @elidupree here. @atagunov's confusion on this proposal and @programmerjake's example which highlights my own misunderstanding of how this ought to be are telling that this proposal isn't as simple as I had thought. Thanks for all the feedback!

That does sound most consistent, unfortunately that's not too useful so I'm unsure how to fix the proposal.

Since String is a type with no lifetimes, it falls under the rule: "All types with no type or lifetime parameters implement for<'a> MaxLifetime<'a>", which means 'a: String for all lifetimes 'a, in particular including the lifetime of v.

To address the purpose of this, it is not enough to tie the lifetime in the return type? Like fn borrow_from<'a,T>(&'a self) -> Bind<'a,T>{...}

playground