-
Notifications
You must be signed in to change notification settings - Fork 505
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
Lifetime elision #187
Merged
Merged
Lifetime elision #187
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
# Lifetime elision | ||
|
||
Rust has rules that allow lifetimes to be elided in various places where the | ||
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 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. | ||
|
||
* 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. | ||
|
||
In method signatures there is another rule | ||
|
||
* 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: | ||
|
||
```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<'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 | ||
``` | ||
|
||
## 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. | ||
|
||
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 | ||
|
||
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... | ||
trait Foo { } | ||
|
||
// These two are the same as Box<T> has no lifetime bound on T | ||
Box<Foo> | ||
Box<Foo + 'static> | ||
|
||
// ...and so are these: | ||
impl Foo {} | ||
impl Foo + 'static {} | ||
|
||
// ...so are these, because &'a T requires T: 'a | ||
&'a Foo | ||
&'a (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> | ||
|
||
// 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 | ||
``` | ||
|
||
Note that the innermost object sets the bound, so `&'a Box<Foo>` is still `&'a | ||
Box<Foo + 'static>`. | ||
|
||
```rust,ignore | ||
// For the following trait... | ||
trait Bar<'a>: 'a { } | ||
|
||
// ...these two are the same: | ||
Box<Bar<'a>> | ||
Box<Bar<'a> + 'a> | ||
|
||
// ...and so are these: | ||
impl<'a> Foo<'a> {} | ||
impl<'a> Foo<'a> + 'a {} | ||
|
||
// This is still an error: | ||
struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b> | ||
TwoBounds<'a, 'b, Foo<'c>> | ||
``` | ||
|
||
## `'static` lifetime elision | ||
|
||
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. | ||
|
||
```rust | ||
// 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: [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. 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 = .. | ||
|
||
// 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 = .. | ||
``` | ||
|
||
[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 | ||
[static]: items.html#static-items | ||
[trait object]: types.html#trait-objects | ||
[type aliases]: items/type-aliases.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how, but it'd be nice for this page to say it's possible and link to the rules for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is at least a mention of this on the constant items page with a link.