]>
Commit | Line | Data |
---|---|---|
0531ce1d XL |
1 | # Lifetime elision |
2 | ||
3 | Rust has rules that allow lifetimes to be elided in various places where the | |
4 | compiler can infer a sensible default choice. | |
5 | ||
6 | ## Lifetime elision in functions | |
7 | ||
94b46f34 | 8 | In order to make common patterns more ergonomic, lifetime arguments can be |
e1599b0c | 9 | *elided* in [function item], [function pointer], and [closure trait] signatures. |
0531ce1d | 10 | The following rules are used to infer lifetime parameters for elided lifetimes. |
94b46f34 XL |
11 | It is an error to elide lifetime parameters that cannot be inferred. The |
12 | placeholder lifetime, `'_`, can also be used to have a lifetime inferred in the | |
13 | same way. For lifetimes in paths, using `'_` is preferred. Trait object | |
14 | lifetimes follow different rules discussed | |
15 | [below](#default-trait-object-lifetimes). | |
0531ce1d XL |
16 | |
17 | * Each elided lifetime in the parameters becomes a distinct lifetime parameter. | |
18 | * If there is exactly one lifetime used in the parameters (elided or not), that | |
19 | lifetime is assigned to *all* elided output lifetimes. | |
20 | ||
21 | In method signatures there is another rule | |
22 | ||
23 | * If the receiver has type `&Self` or `&mut Self`, then the lifetime of that | |
24 | reference to `Self` is assigned to all elided output lifetime parameters. | |
25 | ||
26 | Examples: | |
27 | ||
60c5eb7d XL |
28 | ```rust |
29 | # trait T {} | |
30 | # trait ToCStr {} | |
31 | # struct Thing<'a> {f: &'a i32} | |
32 | # struct Command; | |
33 | # | |
34 | # trait Example { | |
35 | fn print1(s: &str); // elided | |
36 | fn print2(s: &'_ str); // also elided | |
37 | fn print3<'a>(s: &'a str); // expanded | |
38 | ||
39 | fn debug1(lvl: usize, s: &str); // elided | |
40 | fn debug2<'a>(lvl: usize, s: &'a str); // expanded | |
41 | ||
42 | fn substr1(s: &str, until: usize) -> &str; // elided | |
43 | fn substr2<'a>(s: &'a str, until: usize) -> &'a str; // expanded | |
44 | ||
45 | fn get_mut1(&mut self) -> &mut dyn T; // elided | |
46 | fn get_mut2<'a>(&'a mut self) -> &'a mut dyn T; // expanded | |
47 | ||
48 | fn args1<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided | |
49 | fn args2<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded | |
50 | ||
51 | fn new1(buf: &mut [u8]) -> Thing<'_>; // elided - preferred | |
52 | fn new2(buf: &mut [u8]) -> Thing; // elided | |
53 | fn new3<'a>(buf: &'a mut [u8]) -> Thing<'a>; // expanded | |
54 | # } | |
55 | ||
56 | type FunPtr1 = fn(&str) -> &str; // elided | |
57 | type FunPtr2 = for<'a> fn(&'a str) -> &'a str; // expanded | |
58 | ||
59 | type FunTrait1 = dyn Fn(&str) -> &str; // elided | |
60 | type FunTrait2 = dyn for<'a> Fn(&'a str) -> &'a str; // expanded | |
61 | ``` | |
0531ce1d | 62 | |
60c5eb7d XL |
63 | ```rust,compile_fail |
64 | // The following examples show situations where it is not allowed to elide the | |
65 | // lifetime parameter. | |
0531ce1d | 66 | |
60c5eb7d XL |
67 | # trait Example { |
68 | // Cannot infer, because there are no parameters to infer from. | |
69 | fn get_str() -> &str; // ILLEGAL | |
0531ce1d | 70 | |
60c5eb7d XL |
71 | // Cannot infer, ambiguous if it is borrowed from the first or second parameter. |
72 | fn frob(s: &str, t: &str) -> &str; // ILLEGAL | |
73 | # } | |
0531ce1d XL |
74 | ``` |
75 | ||
76 | ## Default trait object lifetimes | |
77 | ||
78 | The assumed lifetime of references held by a [trait object] is called its | |
79 | _default object lifetime bound_. These were defined in [RFC 599] and amended in | |
94b46f34 XL |
80 | [RFC 1156]. |
81 | ||
82 | These default object lifetime bounds are used instead of the lifetime parameter | |
83 | elision rules defined above when the lifetime bound is omitted entirely. If | |
84 | `'_` is used as the lifetime bound then the bound follows the usual elision | |
85 | rules. | |
0531ce1d XL |
86 | |
87 | If the trait object is used as a type argument of a generic type then the | |
88 | containing type is first used to try to infer a bound. | |
89 | ||
90 | * If there is a unique bound from the containing type then that is the default | |
91 | * If there is more than one bound from the containing type then an explicit | |
92 | bound must be specified | |
93 | ||
94 | If neither of those rules apply, then the bounds on the trait are used: | |
95 | ||
96 | * If the trait is defined with a single lifetime _bound_ then that bound is | |
97 | used. | |
98 | * If `'static` is used for any lifetime bound then `'static` is used. | |
99 | * If the trait has no lifetime bounds, then the lifetime is inferred in | |
100 | expressions and is `'static` outside of expressions. | |
101 | ||
60c5eb7d | 102 | ```rust |
0531ce1d XL |
103 | // For the following trait... |
104 | trait Foo { } | |
105 | ||
94222f64 | 106 | // These two are the same because Box<T> has no lifetime bound on T |
60c5eb7d XL |
107 | type T1 = Box<dyn Foo>; |
108 | type T2 = Box<dyn Foo + 'static>; | |
0531ce1d XL |
109 | |
110 | // ...and so are these: | |
94b46f34 XL |
111 | impl dyn Foo {} |
112 | impl dyn Foo + 'static {} | |
0531ce1d XL |
113 | |
114 | // ...so are these, because &'a T requires T: 'a | |
60c5eb7d XL |
115 | type T3<'a> = &'a dyn Foo; |
116 | type T4<'a> = &'a (dyn Foo + 'a); | |
0531ce1d XL |
117 | |
118 | // std::cell::Ref<'a, T> also requires T: 'a, so these are the same | |
60c5eb7d XL |
119 | type T5<'a> = std::cell::Ref<'a, dyn Foo>; |
120 | type T6<'a> = std::cell::Ref<'a, dyn Foo + 'a>; | |
121 | ``` | |
0531ce1d | 122 | |
60c5eb7d XL |
123 | ```rust,compile_fail |
124 | // This is an example of an error. | |
125 | # trait Foo { } | |
126 | struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b> { | |
127 | f1: &'a i32, | |
128 | f2: &'b i32, | |
129 | f3: T, | |
130 | } | |
131 | type T7<'a, 'b> = TwoBounds<'a, 'b, dyn Foo>; | |
132 | // ^^^^^^^ | |
133 | // Error: the lifetime bound for this object type cannot be deduced from context | |
0531ce1d XL |
134 | ``` |
135 | ||
94b46f34 XL |
136 | Note that the innermost object sets the bound, so `&'a Box<dyn Foo>` is still |
137 | `&'a Box<dyn Foo + 'static>`. | |
0531ce1d | 138 | |
60c5eb7d | 139 | ```rust |
0531ce1d XL |
140 | // For the following trait... |
141 | trait Bar<'a>: 'a { } | |
142 | ||
143 | // ...these two are the same: | |
60c5eb7d XL |
144 | type T1<'a> = Box<dyn Bar<'a>>; |
145 | type T2<'a> = Box<dyn Bar<'a> + 'a>; | |
0531ce1d XL |
146 | |
147 | // ...and so are these: | |
60c5eb7d XL |
148 | impl<'a> dyn Bar<'a> {} |
149 | impl<'a> dyn Bar<'a> + 'a {} | |
0531ce1d XL |
150 | ``` |
151 | ||
152 | ## `'static` lifetime elision | |
153 | ||
154 | Both [constant] and [static] declarations of reference types have *implicit* | |
155 | `'static` lifetimes unless an explicit lifetime is specified. As such, the | |
156 | constant declarations involving `'static` above may be written without the | |
157 | lifetimes. | |
158 | ||
159 | ```rust | |
160 | // STRING: &'static str | |
161 | const STRING: &str = "bitstring"; | |
162 | ||
163 | struct BitsNStrings<'a> { | |
164 | mybits: [u32; 2], | |
165 | mystring: &'a str, | |
166 | } | |
167 | ||
168 | // BITS_N_STRINGS: BitsNStrings<'static> | |
94b46f34 | 169 | const BITS_N_STRINGS: BitsNStrings<'_> = BitsNStrings { |
0531ce1d XL |
170 | mybits: [1, 2], |
171 | mystring: STRING, | |
172 | }; | |
173 | ``` | |
174 | ||
175 | Note that if the `static` or `const` items include function or closure | |
176 | references, which themselves include references, the compiler will first try | |
177 | the standard elision rules. If it is unable to resolve the lifetimes by its | |
178 | usual rules, then it will error. By way of example: | |
179 | ||
60c5eb7d XL |
180 | ```rust |
181 | # struct Foo; | |
182 | # struct Bar; | |
183 | # struct Baz; | |
184 | # fn somefunc(a: &Foo, b: &Bar, c: &Baz) -> usize {42} | |
0531ce1d | 185 | // Resolved as `fn<'a>(&'a str) -> &'a str`. |
60c5eb7d | 186 | const RESOLVED_SINGLE: fn(&str) -> &str = |x| x; |
0531ce1d XL |
187 | |
188 | // Resolved as `Fn<'a, 'b, 'c>(&'a Foo, &'b Bar, &'c Baz) -> usize`. | |
60c5eb7d XL |
189 | const RESOLVED_MULTIPLE: &dyn Fn(&Foo, &Bar, &Baz) -> usize = &somefunc; |
190 | ``` | |
0531ce1d | 191 | |
60c5eb7d XL |
192 | ```rust,compile_fail |
193 | # struct Foo; | |
194 | # struct Bar; | |
195 | # struct Baz; | |
196 | # fn somefunc<'a,'b>(a: &'a Foo, b: &'b Bar) -> &'a Baz {unimplemented!()} | |
0531ce1d XL |
197 | // There is insufficient information to bound the return reference lifetime |
198 | // relative to the argument lifetimes, so this is an error. | |
60c5eb7d XL |
199 | const RESOLVED_STATIC: &dyn Fn(&Foo, &Bar) -> &Baz = &somefunc; |
200 | // ^ | |
201 | // this function's return type contains a borrowed value, but the signature | |
202 | // does not say whether it is borrowed from argument 1 or argument 2 | |
0531ce1d XL |
203 | ``` |
204 | ||
416331ca XL |
205 | [closure trait]: types/closure.md |
206 | [constant]: items/constant-items.md | |
207 | [function item]: types/function-item.md | |
208 | [function pointer]: types/function-pointer.md | |
0531ce1d XL |
209 | [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md |
210 | [RFC 1156]: https://github.com/rust-lang/rfcs/blob/master/text/1156-adjust-default-object-bounds.md | |
416331ca XL |
211 | [static]: items/static-items.md |
212 | [trait object]: types/trait-object.md |