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
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
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
stdand possibly other common libs (voluntarily).
- We can add a lint advocating explicitness (with
allowto 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)!