Skip to content

Commit

Permalink
Make corrections/wording changes to elision documents
Browse files Browse the repository at this point in the history
* Don't talk about a future feature
* Add examples of elision in function pointers/traits
* A rule for 'static bounds on trait objects
* Static lifetime elision doesn't happen in function pointers/traits
  • Loading branch information
matthewjasper committed Feb 13, 2018
1 parent 21b4b9f commit 1014b0d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 66 deletions.
119 changes: 55 additions & 64 deletions src/lifetime-elision.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,18 @@ compiler can infer a sensible default choice.
## Lifetime elision in functions

In order to make common patterns more ergonomic, Rust allows lifetimes to be
*elided* in function signatures.
*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.

A *lifetime position* is anywhere you can write a lifetime in a type:
* 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
lifetime is assigned to *all* elided output lifetimes.

```rust,ignore
&'a T
&'a mut T
T<'a>
```

Lifetime positions can appear as either "input" or "output":

* For `fn` definitions, input refers to the types of the formal arguments
in the `fn` definition, while output refers to
result types. So `fn foo(s: &str) -> (&str, &str)` has elided one lifetime in
input position and two lifetimes in output position.
Note that the input positions of a `fn` method definition do not
include the lifetimes that occur in the method's `impl` header
(nor lifetimes that occur in the trait header, for a default method).

* In the future, it should be possible to elide `impl` headers in the same manner.

Elision rules are as follows:

* Each elided lifetime in input position becomes a distinct lifetime
parameter.

* If there is exactly one input lifetime position (elided or not), that lifetime
is assigned to *all* elided output lifetimes.
In method signatures there is another rule

* If there are multiple input lifetime positions, but one of them is `&self` or
`&mut self`, the lifetime of `self` is assigned to *all* elided output lifetimes.

* Otherwise, it is an error to elide an output lifetime.
* If the receiver has type `&Self` or `&mut Self`, then the lifetime of that
reference to `Self` is assigned to all elided output lifetime parameters.

Examples:

Expand All @@ -60,25 +38,40 @@ fn frob(s: &str, t: &str) -> &str; // ILLEGAL
fn get_mut(&mut self) -> &mut T; // elided
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 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
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded
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
```

## Defaults trait object lifetimes
## 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].
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.

For traits that themselves have no lifetime parameters:
* If there is a unique bound from the containing type then that is the default.
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.

* If there is a unique bound from the containing type then that is the default
* If there is more than one bound from the containing type then an explicit
bound must be specified.
* Otherwise the default bound is `'static`.
bound must be specified

If neither of those rules apply, then the bounds on the trait are used:

* If the trait is defined with a single lifetime _bound_ then that bound is
used.
* If `'static` is used for any lifetime bound then `'static` is used.
* If the trait has no lifetime bounds, then the lifetime is inferred in
expressions and is `'static` outside of expressions.

```rust,ignore
// For the following trait...
Expand All @@ -104,15 +97,10 @@ std::cell::Ref<'a, Foo + 'a>
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
```

The `+ 'static` and `+ 'a` refer to the default bounds of those kinds of trait
objects, and also to how you can directly override them. Note that the innermost
object sets the bound, so `&'a Box<Foo>` is still `&'a Box<Foo + 'static>`.

For traits that have a single lifetime _bound_ of their own then, instead of
infering 'static as the default bound, the bound on the trait is used instead
Note that the innermost object sets the bound, so `&'a Box<Foo>` is still `&'a
Box<Foo + 'static>`.

```rust,ignore
// For the following trait...
Expand All @@ -133,48 +121,51 @@ TwoBounds<'a, 'b, Foo<'c>>

## `'static` lifetime elision

Both constant and static declarations of reference types have *implicit*
Both [constant] and [static] declarations of reference types have *implicit*
`'static` lifetimes unless an explicit lifetime is specified. As such, the
constant declarations involving `'static` above may be written without the
lifetimes. Returning to our previous example:
lifetimes.

```rust
const BIT1: u32 = 1 << 0;
const BIT2: u32 = 1 << 1;

const BITS: [u32; 2] = [BIT1, BIT2];
// STRING: &'static str
const STRING: &str = "bitstring";

struct BitsNStrings<'a> {
mybits: [u32; 2],
mystring: &'a str,
}

// BITS_N_STRINGS: BitsNStrings<'static>
const BITS_N_STRINGS: BitsNStrings = BitsNStrings {
mybits: BITS,
mybits: [1, 2],
mystring: STRING,
};
```

Note that if the `static` or `const` items include function or closure
references, which themselves include references, the compiler will first try
the standard elision rules ([see discussion in the nomicon][elision-nomicon]).
If it is unable to resolve the lifetimes by its usual rules, it will default to
using the `'static` lifetime. By way of example:
the standard elision rules. If it is unable to resolve the lifetimes by its
usual rules, then it will error. By way of example:

```rust,ignore
// Resolved as `fn<'a>(&'a str) -> &'a str`.
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: &Fn(&Foo, &Bar, &Baz) -> usize = ..
// There is insufficient information to bound the return reference lifetime
// relative to the argument lifetimes, so the signature is resolved as
// `Fn(&'static Foo, &'static Bar) -> &'static Baz`.
const RESOLVED_STATIC: Fn(&Foo, &Bar) -> &Baz = ..
// relative to the argument lifetimes, so this is an error.
const RESOLVED_STATIC: &Fn(&Foo, &Bar) -> &Baz = ..
```

[closure trait]: types.html#closure-types
[constant]: items.html#constant-items
[function item]: types.html#function-item-types
[function pointer]: types.html#function-pointers
[implementation]: items/implementations.html
[RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
[RFC 1156]: https://github.com/rust-lang/rfcs/blob/master/text/1156-adjust-default-object-bounds.md
[elision-nomicon]: ../nomicon/lifetime-elision.html
[static]: items.html#static-items
[trait object]: types.html#trait-objects
[type aliases]: items/type-aliases.html
2 changes: 0 additions & 2 deletions src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,6 @@ The notation `&self` is a shorthand for `self: &Self`.
[`Vec<T>`]: ../std/vec/struct.Vec.html
[dynamically sized type]: dynamically-sized-types.html
[dynamically sized types]: dynamically-sized-types.html
[RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md
[RFC 1156]: https://github.com/rust-lang/rfcs/blob/master/text/1156-adjust-default-object-bounds.md
[struct expression]: expressions/struct-expr.html
[closure expression]: expressions/closure-expr.html
[auto traits]: special-types-and-traits.html#auto-traits
Expand Down

0 comments on commit 1014b0d

Please sign in to comment.