Confusing lifetime elision of returned structs


#1

There is one unclear type of confusion with borrow happening or not due to lifetime elision on returned structs. I encounter it regularly and from my experience over a year ago, this can confuse beginners struggling with the borrow checker.

Let me illustrate with an easy example:

let mut v = vec![1,2,3];
let mut c = v.clone(); // OK
let mut d = v.iter(); // Conflict with the next line
drop(v); // d still borrowed
// ... use c and d somehow

The compiler error informs you that there is a borrow going on but since clone and iter have an almost identical signature (inside impl Vec<T>) in the sources as well as in the docs:

fn iter(&self) -> Iter<T>
fn clone(&self) -> Vec<T>

it looks a bit like magic. That is until you notice that struct Iter has a lifetime parameter that makes the expanded signature

fn<'a> iter(&'a self) -> Iter<'a, T>

With the lifetimes, the borrow is clear but without it, Iter looks like a standalone struct. In order to notice that, you need to look at the source or docs of struct Iter (and a beginner would need to know what to look for).

This is a common scenario when creating “proxy” structs such as iterators, drains, guards, combinators etc. It may be clear from the context with things like .iter() but is less clear with less familiar things like .drain() (which may or may not borrow the original struct, depending on the type). Perhaps you know better examples :slight_smile:

Note that elision in cases such as fn first(&[T]) -> Option<&T> is not problematic IMO because it is clear that &T has to have a bounded lifetime. It is the elision on Iter<'a, T> that is the problem. This is also briefly mentioned in the RFC but without much discussion.

I am bringing it up mainly because I would hope this to be easy to significantly improve:

  • We can make it a best practice to include lifetimes of returned structs.
  • Based on that, add lifetime annotations to such cases in std and possibly other common libs (voluntarily).
  • We can add a lint advocating explicitness (with allow to see the impact at first and to be used in participating libs).

My intention is not to forbid this type of elision but rather to make it beginner and library-user friendlier in the most common cases.

I am looking for your view or other solutions (and any discussions I may have missed)!


#2

I believe this is covered by accepted RFC 2115. The warning has been implemented in nightly— #![warn(elided_lifetime_in_path)] —and is being tracked as part of rust-lang/rust#44524.


#3

Hi David, thanks for the quick reply! I missed that RFC you linked (and an ergonomics blog post linked there) while searching, but it pretty much solves the problem without lengthy lifetime notation :slight_smile: