|
|
|
# Lifetime Elision
|
|
|
|
|
|
|
|
In order to make common patterns more ergonomic, Rust allows lifetimes to be
|
|
|
|
*elided* in function signatures.
|
|
|
|
|
|
|
|
A *lifetime position* is anywhere you can write a lifetime in a type:
|
|
|
|
|
|
|
|
<!-- ignore: simplified code -->
|
|
|
|
```rust,ignore
|
|
|
|
&'a T
|
|
|
|
&'a mut T
|
|
|
|
T<'a>
|
|
|
|
```
|
|
|
|
|
|
|
|
Lifetime positions can appear as either "input" or "output":
|
|
|
|
|
Document lifetime elision for fn types, Fn*, impl
Currently, the lifetime elision doc only documents function definitions, but lifetime elision is also allowed in the following other locations:
* `fn` types, such as `fn(&T)`
* `Fn`/`FnMut`/`FnOnce`, such as `Fn(&T)`
* `impl` headers
To demo this up, I made some type aliases for `fn`/`Fn` which you can pass `&T` as a parameter to (to follow the lifetime rules of the surrounding context), and compared what you get with that instead of using `fn`/`Fn` directly, where lifetime elision takes on the rules from `fn`/`Fn`/etc.
I also demoed up an `impl` header that used lifetime elision twice, although the error message in that case is broken (filed https://github.com/rust-lang/rust/issues/87763)
The demo was half for this change description, and half just to make sure I understand Rust -- in particular, I really had to reverse engineer it for `impl` because I wasn't sure, and it didn't seem to be documented anywhere (at least not here!)
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f82b280de4b992f225bc32121f333e96
3 years ago
|
|
|
* For `fn` definitions, `fn` types, and the traits `Fn`, `FnMut`, and `FnOnce`,
|
|
|
|
input refers to the types of the formal arguments, while output refers to
|
|
|
|
result types. So `fn foo(s: &str) -> (&str, &str)` has elided one lifetime in
|
Document lifetime elision for fn types, Fn*, impl
Currently, the lifetime elision doc only documents function definitions, but lifetime elision is also allowed in the following other locations:
* `fn` types, such as `fn(&T)`
* `Fn`/`FnMut`/`FnOnce`, such as `Fn(&T)`
* `impl` headers
To demo this up, I made some type aliases for `fn`/`Fn` which you can pass `&T` as a parameter to (to follow the lifetime rules of the surrounding context), and compared what you get with that instead of using `fn`/`Fn` directly, where lifetime elision takes on the rules from `fn`/`Fn`/etc.
I also demoed up an `impl` header that used lifetime elision twice, although the error message in that case is broken (filed https://github.com/rust-lang/rust/issues/87763)
The demo was half for this change description, and half just to make sure I understand Rust -- in particular, I really had to reverse engineer it for `impl` because I wasn't sure, and it didn't seem to be documented anywhere (at least not here!)
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f82b280de4b992f225bc32121f333e96
3 years ago
|
|
|
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).
|
|
|
|
|
|
|
|
* For `impl` headers, all types are input. So `impl Trait<&T> for Struct<&T>`
|
|
|
|
has elided two lifetimes in input position, while `impl Struct<&T>` has elided
|
|
|
|
one.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
<!-- ignore: simplified code -->
|
|
|
|
```rust,ignore
|
|
|
|
fn print(s: &str); // elided
|
|
|
|
fn print<'a>(s: &'a str); // expanded
|
|
|
|
|
|
|
|
fn debug(lvl: usize, s: &str); // elided
|
|
|
|
fn debug<'a>(lvl: usize, s: &'a str); // expanded
|
|
|
|
|
|
|
|
fn substr(s: &str, until: usize) -> &str; // elided
|
|
|
|
fn substr<'a>(s: &'a str, until: usize) -> &'a str; // expanded
|
|
|
|
|
|
|
|
fn get_str() -> &str; // ILLEGAL
|
|
|
|
|
|
|
|
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 new(buf: &mut [u8]) -> BufWriter; // elided
|
|
|
|
fn new(buf: &mut [u8]) -> BufWriter<'_>; // elided (with `rust_2018_idioms`)
|
|
|
|
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded
|
|
|
|
```
|