@@ -60,6 +60,7 @@ read-write lock.
60
60
61
61
62
62
63
+
63
64
## Lifetimes
64
65
65
66
Rust's static checks are managed by the * borrow checker* (borrowck), which tracks
@@ -219,6 +220,77 @@ these are unstable due to their awkward nature and questionable utility.
219
220
220
221
221
222
223
+
224
+ ## Higher-Rank Lifetimes
225
+
226
+ Generics in Rust generally allow types to be instantiated with arbitrary
227
+ associated lifetimes, but this fixes the lifetimes they work with once
228
+ instantiated. For almost all types, this is exactly the desired behaviour.
229
+ For example slice::Iter can work with arbitrary lifetimes, determined by the
230
+ slice that instantiates it. However * once* Iter is instantiated the lifetimes
231
+ it works with cannot be changed. It returns references that live for some
232
+ particular ` 'a ` .
233
+
234
+ However some types are more flexible than this. In particular, a single
235
+ instantiation of a function can process arbitrary lifetimes:
236
+
237
+ ``` rust
238
+ fn identity (input : & u8 ) -> & u8 { input }
239
+ ```
240
+
241
+ What is * the* lifetime that identity works with? There is none. If you think
242
+ this is "cheating" because functions are statically instantiated, then you need
243
+ only consider the equivalent closure:
244
+
245
+ ``` rust
246
+ let identity = | input : & u8 | input ;
247
+ ```
248
+
249
+ These functions are * higher ranked* over the lifetimes they work with. This means
250
+ that they're generic over what they handle * after instantiation* . For most things
251
+ this would pose a massive problem, but because lifetimes don't * exist* at runtime,
252
+ this is really just a compile-time mechanism. The Fn traits contain sugar that
253
+ allows higher-rank lifetimes to simply be expressed by simply omitting lifetimes:
254
+
255
+
256
+ ``` rust
257
+ fn main () {
258
+ foo (| input | input );
259
+ }
260
+
261
+ fn foo <F >(f : F )
262
+ // F is higher-ranked over the lifetime these references have
263
+ where F : Fn (& u8 ) -> & u8
264
+ {
265
+ f (& 0 );
266
+ f (& 1 );
267
+ }
268
+ ```
269
+
270
+ The desugaring of this is actually unstable:
271
+
272
+ ```
273
+ #![feature(unboxed_closures)]
274
+
275
+ fn main() {
276
+ foo(|input| input);
277
+ }
278
+
279
+ fn foo<F>(f: F)
280
+ where F: for<'a> Fn<(&'a u8,), Output=&'a u8>
281
+ {
282
+ f(&0);
283
+ f(&1);
284
+ }
285
+ ```
286
+
287
+ ` for<'a> ` is how we declare a higher-ranked lifetime. Unfortunately higher-ranked
288
+ lifetimes are still fairly new, and are missing a few features to make them
289
+ maximally useful outside of the Fn traits.
290
+
291
+
292
+
293
+
222
294
## Subtyping and Variance
223
295
224
296
Although Rust doesn't have any notion of inheritance, it * does* include subtyping.
@@ -227,12 +299,15 @@ from scopes, we can partially order them based on an *outlives* relationship. We
227
299
can even express this as a generic bound: ` T: 'a ` specifies that ` T ` * outlives* ` 'a ` .
228
300
229
301
We can then define subtyping on lifetimes in terms of lifetimes: ` 'a : 'b ` implies
230
- ` 'a <: b ` -- if ` 'a' outlives ` 'b` , then ` 'a` is a subtype of ` 'b`. This is a very
302
+ ` 'a <: b ` -- if ` 'a ` outlives ` 'b ` , then ` 'a ` is a subtype of ` 'b ` . This is a very
231
303
large source of confusion, because a bigger scope is a * sub type* of a smaller scope.
232
304
This does in fact make sense. The intuitive reason for this is that if you expect an
233
305
` &'a u8 ` , then it's totally fine for me to hand you an ` &'static u8 ` , in the same way
234
306
that if you expect an Animal in Java, it's totally fine for me to hand you a Cat.
235
307
308
+ (Note, the subtyping relationship and typed-ness of lifetimes is a fairly arbitrary
309
+ construct that some disagree with. I just find that it simplifies this analysis.)
310
+
236
311
Variance is where things get really harsh.
237
312
238
313
Variance is a property that * type constructors* have. A type constructor in Rust
@@ -278,7 +353,7 @@ fn overwrite<T: Copy>(input: &mut T, new: &mut T) {
278
353
279
354
The signature of ` overwrite ` is clearly valid: it takes mutable references to two values
280
355
of the same type, and replaces one with the other. We have seen already that ` & ` is
281
- covariant, and ` 'static ` is a subtype of * any* ` 'a' , so ` &'static str` is a
356
+ covariant, and ` 'static ` is a subtype of * any* ` 'a ` , so ` &'static str ` is a
282
357
subtype of ` &'a str ` . Therefore, if ` &mut ` was
283
358
* also* covariant, then the lifetime of the ` &'static str ` would successfully be
284
359
"shrunk" down to the shorter lifetime of the string, and ` replace ` would be
@@ -341,8 +416,16 @@ respectively.
341
416
## PhantomData and PhantomFn
342
417
343
418
This is all well and good for the types the standard library provides, but
344
- how is variance determined for type that * you* define? The variance of a type
345
- over its generic arguments is determined by how they're stored.
419
+ how is variance determined for type that * you* define? A struct is, informally
420
+ speaking, covariant over all its fields (and an enum over its variants). This
421
+ basically means that it inherits the variance of its fields. If a struct ` Foo `
422
+ has a generic argument ` A ` that is used in a field ` a ` , then Foo's variance
423
+ over ` A ` is exactly ` a ` 's variance. However this is complicated if ` A ` is used
424
+ in multiple fields.
425
+
426
+ * If all uses of A are covariant, then Foo is covariant over A
427
+ * If all uses of A are contravariant, then Foo is contravariant over A
428
+ * Otherwise, Foo is invariant over A
346
429
347
430
``` rust
348
431
struct Foo <'a , 'b , A , B , C , D , E , F , G , H > {
@@ -360,7 +443,7 @@ struct Foo<'a, 'b, A, B, C, D, E, F, G, H> {
360
443
361
444
However when working with unsafe code, we can often end up in a situation where
362
445
types or lifetimes are logically associated with a struct, but not actually
363
- reachable . This most commonly occurs with lifetimes. For instance, the ` Iter `
446
+ part of a field . This most commonly occurs with lifetimes. For instance, the ` Iter `
364
447
for ` &'a [T] ` is (approximately) defined as follows:
365
448
366
449
```
0 commit comments