diff --git a/src/lifetime-elision.md b/src/lifetime-elision.md index 6c174a2a8..54060405f 100644 --- a/src/lifetime-elision.md +++ b/src/lifetime-elision.md @@ -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: @@ -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(&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(&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... @@ -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` 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 +Note that the innermost object sets the bound, so `&'a Box` is still `&'a +Box`. ```rust,ignore // For the following trait... @@ -133,16 +121,13 @@ 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> { @@ -150,31 +135,37 @@ struct BitsNStrings<'a> { 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 diff --git a/src/types.md b/src/types.md index f6f5e71d0..46582bff7 100644 --- a/src/types.md +++ b/src/types.md @@ -525,8 +525,6 @@ 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