From 21b4b9fce2bf2a18c5a05ad491f65a0f2e4ab74b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 7 Jan 2018 17:06:28 +0000 Subject: [PATCH 1/2] Copy/move lifetime elision document from other places --- src/SUMMARY.md | 1 + src/items/constant-items.md | 2 +- src/items/static-items.md | 45 --------- src/lifetime-elision.md | 180 ++++++++++++++++++++++++++++++++++++ src/types.md | 65 +------------ 5 files changed, 187 insertions(+), 106 deletions(-) create mode 100644 src/lifetime-elision.md 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..6c174a2a8 --- /dev/null +++ b/src/lifetime-elision.md @@ -0,0 +1,180 @@ +# 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 signatures. + +A *lifetime position* is anywhere you can write a lifetime in a type: + +```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. + +* 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: + +```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 + +``` + +## Defaults 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]. + +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>> +``` + +## `'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 = .. +``` + +[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 diff --git a/src/types.md b/src/types.md index f50e13e58..f6f5e71d0 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 @@ -587,4 +532,4 @@ The notation `&self` is a shorthand for `self: &Self`. [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 From 1014b0d4a0ef7772b7aefdd5faf6b559a2bdf0bf Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Tue, 13 Feb 2018 22:38:35 +0000 Subject: [PATCH 2/2] Make corrections/wording changes to elision documents * 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 --- src/lifetime-elision.md | 119 +++++++++++++++++++--------------------- src/types.md | 2 - 2 files changed, 55 insertions(+), 66 deletions(-) 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