Skip to content

Document underscore lifetime elision #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 36 additions & 26 deletions src/lifetime-elision.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ compiler can infer a sensible default choice.

## Lifetime elision in functions

In order to make common patterns more ergonomic, Rust allows lifetimes to be
In order to make common patterns more ergonomic, lifetime arguments can be
*elided* in [function item], [function pointer] and [closure trait] signatures.
The following rules are used to infer lifetime parameters for elided lifetimes.
It is an error to elide lifetime parameters that cannot be inferred.
It is an error to elide lifetime parameters that cannot be inferred. The
placeholder lifetime, `'_`, can also be used to have a lifetime inferred in the
same way. For lifetimes in paths, using `'_` is preferred. Trait object
lifetimes follow different rules discussed
[below](#default-trait-object-lifetimes).

* Each elided lifetime in the parameters becomes a distinct lifetime parameter.
* If there is exactly one lifetime used in the parameters (elided or not), that
Expand All @@ -23,6 +27,7 @@ Examples:

```rust,ignore
fn print(s: &str); // elided
fn print(s: &'_ str); // also elided
fn print<'a>(s: &'a str); // expanded

fn debug(lvl: usize, s: &str); // elided
Expand All @@ -41,22 +46,27 @@ fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded
fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided
fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded

fn new(buf: &mut [u8]) -> BufWriter<'_>; // elided - preferred
fn new(buf: &mut [u8]) -> BufWriter; // elided
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a>; // expanded

type FunPtr = fn(&str) -> &str; // elided
type FunPtr = for<'a> fn(&'a str) -> &'a str; // expanded

type FunTrait = Fn(&str) -> &str; // elided
type FunTrait = for<'a> Fn(&'a str) -> &'a str; // expanded
type FunTrait = dyn Fn(&str) -> &str; // elided
type FunTrait = dyn for<'a> Fn(&'a str) -> &'a str; // expanded
```

## Default trait object lifetimes

The assumed lifetime of references held by a [trait object] is called its
_default object lifetime bound_. These were defined in [RFC 599] and amended in
[RFC 1156]. Default object lifetime bounds are used instead of the lifetime
parameter elision rules defined above.
[RFC 1156].

These default object lifetime bounds are used instead of the lifetime parameter
elision rules defined above when the lifetime bound is omitted entirely. If
`'_` is used as the lifetime bound then the bound follows the usual elision
rules.

If the trait object is used as a type argument of a generic type then the
containing type is first used to try to infer a bound.
Expand All @@ -78,45 +88,45 @@ If neither of those rules apply, then the bounds on the trait are used:
trait Foo { }

// These two are the same as Box<T> has no lifetime bound on T
Box<Foo>
Box<Foo + 'static>
Box<dyn Foo>
Box<dyn Foo + 'static>

// ...and so are these:
impl Foo {}
impl Foo + 'static {}
impl dyn Foo {}
impl dyn Foo + 'static {}

// ...so are these, because &'a T requires T: 'a
&'a Foo
&'a (Foo + 'a)
&'a dyn Foo
&'a (dyn Foo + 'a)

// std::cell::Ref<'a, T> also requires T: 'a, so these are the same
std::cell::Ref<'a, Foo>
std::cell::Ref<'a, Foo + 'a>
std::cell::Ref<'a, dyn Foo>
std::cell::Ref<'a, dyn Foo + 'a>

// This is an error:
struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b>
TwoBounds<'a, 'b, Foo> // Error: the lifetime bound for this object type cannot
// be deduced from context
TwoBounds<'a, 'b, dyn Foo> // Error: the lifetime bound for this object type
// cannot be deduced from context
```

Note that the innermost object sets the bound, so `&'a Box<Foo>` is still `&'a
Box<Foo + 'static>`.
Note that the innermost object sets the bound, so `&'a Box<dyn Foo>` is still
`&'a Box<dyn Foo + 'static>`.

```rust,ignore
// For the following trait...
trait Bar<'a>: 'a { }

// ...these two are the same:
Box<Bar<'a>>
Box<Bar<'a> + 'a>
Box<dyn Bar<'a>>
Box<dyn Bar<'a> + 'a>

// ...and so are these:
impl<'a> Foo<'a> {}
impl<'a> Foo<'a> + 'a {}
impl<'a> dyn Foo<'a> {}
impl<'a> dyn Foo<'a> + 'a {}

// This is still an error:
struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b>
TwoBounds<'a, 'b, Foo<'c>>
TwoBounds<'a, 'b, dyn Foo<'c>>
```

## `'static` lifetime elision
Expand All @@ -136,7 +146,7 @@ struct BitsNStrings<'a> {
}

// BITS_N_STRINGS: BitsNStrings<'static>
const BITS_N_STRINGS: BitsNStrings = BitsNStrings {
const BITS_N_STRINGS: BitsNStrings<'_> = BitsNStrings {
mybits: [1, 2],
mystring: STRING,
};
Expand All @@ -152,11 +162,11 @@ usual rules, then it will error. By way of example:
const RESOLVED_SINGLE: fn(&str) -> &str = ..

// Resolved as `Fn<'a, 'b, 'c>(&'a Foo, &'b Bar, &'c Baz) -> usize`.
const RESOLVED_MULTIPLE: &Fn(&Foo, &Bar, &Baz) -> usize = ..
const RESOLVED_MULTIPLE: &dyn Fn(&Foo, &Bar, &Baz) -> usize = ..

// There is insufficient information to bound the return reference lifetime
// relative to the argument lifetimes, so this is an error.
const RESOLVED_STATIC: &Fn(&Foo, &Bar) -> &Baz = ..
const RESOLVED_STATIC: &dyn Fn(&Foo, &Bar) -> &Baz = ..
```

[closure trait]: types.html#closure-types
Expand Down
1 change: 1 addition & 0 deletions src/trait-bounds.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
> _Lifetime_ :
> &nbsp;&nbsp; &nbsp;&nbsp; [LIFETIME_OR_LABEL]
> &nbsp;&nbsp; | `'static`
> &nbsp;&nbsp; | `'_`

[Trait] and lifetime bounds provide a way for [generic items][generic] to
restrict which types and lifetimes are used as their parameters. Bounds can be
Expand Down