diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 642e4b483..bb035113f 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -66,6 +66,7 @@ - [Subtyping](subtyping.md) - [Type coercions](type-coercions.md) - [Destructors](destructors.md) + - [Lifetime elision](lifetime-elision.md) - [Special types and traits](special-types-and-traits.md) diff --git a/src/items/constant-items.md b/src/items/constant-items.md index 0800aa106..d2cc70116 100644 --- a/src/items/constant-items.md +++ b/src/items/constant-items.md @@ -61,7 +61,7 @@ fn create_and_drop_zero_with_destructor() { ``` [constant value]: expressions.html#constant-expressions -[static lifetime elision]: items/static-items.html#static-lifetime-elision +[static lifetime elision]: items/lifetime-elision.html#static-lifetime-elision [`Drop`]: special-types-and-traits.html#drop [IDENTIFIER]: identifiers.html [_Type_]: types.html diff --git a/src/items/static-items.md b/src/items/static-items.md index a11acd13f..c1d47c878 100644 --- a/src/items/static-items.md +++ b/src/items/static-items.md @@ -59,50 +59,6 @@ unsafe fn bump_levels_unsafe2() -> u32 { Mutable statics have the same restrictions as normal statics, except that the type does not have to implement the `Sync` trait. -## `'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. Returning to our previous example: - -```rust -const BIT1: u32 = 1 << 0; -const BIT2: u32 = 1 << 1; - -const BITS: [u32; 2] = [BIT1, BIT2]; -const STRING: &str = "bitstring"; - -struct BitsNStrings<'a> { - mybits: [u32; 2], - mystring: &'a str, -} - -const BITS_N_STRINGS: BitsNStrings = BitsNStrings { - mybits: BITS, - 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: - -```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 the signature is resolved as -// `Fn(&'static Foo, &'static Bar) -> &'static Baz`. -const RESOLVED_STATIC: Fn(&Foo, &Bar) -> &Baz = .. -``` - ## Using Statics or Consts In can be confusing whether or not you should use a constant item or a static @@ -118,4 +74,3 @@ following are true: [IDENTIFIER]: identifiers.html [_Type_]: types.html [_Expression_]: expressions.html -[elision-nomicon]: ../nomicon/lifetime-elision.html \ No newline at end of file diff --git a/src/lifetime-elision.md b/src/lifetime-elision.md new file mode 100644 index 000000000..54060405f --- /dev/null +++ b/src/lifetime-elision.md @@ -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(&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 has no lifetime bound on T +Box +Box + +// ...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` is still `&'a +Box`. + +```rust,ignore +// For the following trait... +trait Bar<'a>: 'a { } + +// ...these two are the same: +Box> +Box + '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 diff --git a/src/types.md b/src/types.md index f50e13e58..46582bff7 100644 --- a/src/types.md +++ b/src/types.md @@ -462,66 +462,11 @@ type signature of `print`, and the cast expression in `main`. ### Trait Object Lifetime Bounds Since a trait object can contain references, the lifetimes of those references -need to be expressed as part of the trait object. 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]. +need to be expressed as part of the trait object. This lifetime is written as +`Trait + 'a`. There are [defaults] that allow this lifetime to usually be +infered with a sensible choice. -For traits that themselves have no lifetime parameters: -* 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`. - -```rust,ignore -// For the following trait... -trait Foo { } - -// These two are the same as Box has no lifetime bound on T -Box -Box - -// ...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 - -``` - -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` is still `&'a Box`. - -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 - -```rust,ignore -// For the following trait... -trait Bar<'a>: 'a { } - -// ...these two are the same: -Box> -Box + '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>> -``` +[defaults]: lifetime-elision.html#elision-and-defaults-in-trait-objects ## Type parameters @@ -580,11 +525,9 @@ The notation `&self` is a shorthand for `self: &Self`. [`Vec`]: ../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 [object safe]: items/traits.html#object-safety [issue 47010]: https://github.com/rust-lang/rust/issues/47010 -[issue 33140]: https://github.com/rust-lang/rust/issues/33140 \ No newline at end of file +[issue 33140]: https://github.com/rust-lang/rust/issues/33140