Proposal: Lifetime elision for HRTB


#1

A common use of higher-ranked trait bounds (HRTB) is to place bounds on reference types. For example:

fn run<V>(items: V) where for<'a> &'a V: IntoIterator<Item=&'a Foo>

Would it be possible to add elision rules where lifetimes in the type are “inputs” and lifetimes in the bound are “outputs” so this could be simplified to:

fn run<V>(items: V) where &V: IntoIterator<Item=&Foo>

(Currently this generates a “missing lifetime specifier” error.)

Note: The Fn traits already allow elided lifetimes in the bound, and these would need to retain their current meanings.


#2

Are such constructions common with respect to usual Rust code?

I’ve been programming in Rust for a while, and this is the first time I see this pattern. It definitely took me a while to grasp why you need HRTB here at all.

The example with elided for looks simpler, but can be very perplexing if you don’t know what’s going on.

This reminds me of a similar elision feature, where Box<dyn Foo> is actually Box<dyn Foo + 'static>. Every once in a while somebody on urlo is confused by this, arguably because elision make it difficult to see that something is going on here at all.


#3

I ripgrepped through some large Rust projects (including rustc, Servo, and all the Rust crates used in Firefox) and found very few instances that would be helped by this elision rule. So maybe it’s not common enough to be worthwhile.


#4

Potential unintuitive inference time!

fn run<'a, V>(items: &'a V) where &V: IntoIterator<Item=&Foo>

Is the above this:

fn run<'a, V>(items: &'a V) where &'a V: IntoIterator<Item=&'a Foo>

Or is it this:

fn run<'a, V>(items: &'a V) where for<'b> &'b V: IntoIterator<Item=&'b Foo>

?


#5

It’s the latter. An elided lifetime always results in a new lifetime being inserted by the compiler; it’s never “unified” with an existing explicit lifetime.

For example, in current Rust,

fn run<'a>(&self, x: &'a Foo) -> &i32

is equivalent to:

fn run<'a, 'b>(&'b self, x: &'a Foo) -> &'b i32