IDEA: unify lifetime in function signature

Many times, I found the need to ask the compiler to unify the lifetime in function signatures.

For example, the signature below

trait User {
    fn update_user_name(&self, name: &str) -> Pin<Box<dyn Future<Output=()> + Send + '_>>>;
}

if having the (unnamed) lifetimes unified, it will be like

trait User {
    fn update_user_name<'a>(&'a self, name: &'a str) -> Pin<Box<dyn Future<Output=()> + Send + 'a>>>;
}

(Note the above does not declared as async because, if you want this to be a method of a dyn object that's what you have to write without async-trait crate)

The other use case is when using arenas like bumpalo, you almost certain will end up with a lot of things like &'a MyStruct<'a> where the 'a is the lifetime of the arena. And then your method signatures will have more lifetimes to unify.

My proposal is to allow an anotation #[unify_lifetime] to indicate every implicit lifetime in the signature are the same.

So the above example (the unified one) is written

trait User {
    #[unify_lifetime]
    fn update_user_name(&self, name: &str) -> Pin<Box<dyn Future<Output=()> + Send + '_>>>;
}

To my (limited of cause) experience, this is already way too common - almost every time when I have to manually introduce lifetime arguments I don't have more than one to add.

This is why I believe this small change can significantly increase ergonomic, and flatten the learning curve for new users.

For the implementation bit, I believe a simple proc-macro can do the job. But if it is in std, the compiler message can be updated to prompt the user to use this, further lowering the learning curve.

I am doubtful that this would be easier for new users. It's adding a new way to write something that's already possible, and it’s hard to explain when it should be applied without making reference to the details that it’s hiding.

And, if someone is in the habit of using #[unify_lifetime] and they meet a situation where they need to not unify a lifetime, it will be harder to get there. In my experience, the errors that result from using the same lifetime too many places are harder to understand than the errors that result from using too many different lifetimes. If you have separate lifetimes, you can get an error pointing to the two you need to merge or constrain; if you have the same lifetime but need separate ones, you get an error that can’t tell you what to separate.

13 Likes