You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -102,59 +102,11 @@ more than a local lint against incorrect usage of a value.
102
102
103
103
104
104
105
-
# Weird Lifetimes
106
-
107
-
Given the following code:
108
-
109
-
```rust
110
-
structFoo;
111
-
112
-
implFoo {
113
-
fnmutate_and_share(&mutself) ->&Self { &*self }
114
-
fnshare(&self) {}
115
-
}
116
-
117
-
fnmain() {
118
-
letmutfoo=Foo;
119
-
letloan=foo.mutate_and_share();
120
-
foo.share();
121
-
}
122
-
```
123
-
124
-
One might expect it to compile. We call `mutate_and_share`, which mutably borrows
125
-
`foo`*temporarily*, but then returns *only* a shared reference. Therefore we
126
-
would expect `foo.share()` to succeed as `foo` shouldn't be mutably borrowed.
127
-
128
-
However when we try to compile it:
129
-
130
-
```text
131
-
<anon>:11:5: 11:8 error: cannot borrow `foo` as immutable because it is also borrowed as mutable
132
-
<anon>:11 foo.share();
133
-
^~~
134
-
<anon>:10:16: 10:19 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
135
-
<anon>:10 let loan = foo.mutate_and_share();
136
-
^~~
137
-
<anon>:12:2: 12:2 note: previous borrow ends here
138
-
<anon>:8 fn main() {
139
-
<anon>:9 let mut foo = Foo;
140
-
<anon>:10 let loan = foo.mutate_and_share();
141
-
<anon>:11 foo.share();
142
-
<anon>:12 }
143
-
^
144
-
```
145
-
146
-
What happened? Well, the lifetime of `loan` is derived from a *mutable* borrow.
147
-
This makes the type system believe that `foo` is mutably borrowed as long as
148
-
`loan` exists, even though it's a shared reference. To my knowledge, this is not
149
-
a bug.
150
-
151
-
152
-
153
105
154
106
# Lifetime Elision
155
107
156
108
In order to make common patterns more ergonomic, Rust allows lifetimes to be
157
-
*elided* in function, impl, and type signatures.
109
+
*elided* in function signatures.
158
110
159
111
A *lifetime position* is anywhere you can write a lifetime in a type:
160
112
@@ -336,16 +288,18 @@ In Rust, subtyping derives entirely from *lifetimes*. Since lifetimes are derive
336
288
from scopes, we can partially order them based on an *outlives* relationship. We
337
289
can even express this as a generic bound: `T: 'a` specifies that `T`*outlives*`'a`.
338
290
339
-
We can then define subtyping on lifetimes in terms of lifetimes: `'a : 'b` implies
340
-
`'a <: b` -- if `'a`outlives `'b`, then `'a` is a subtype of `'b`. This is a very
291
+
We can then define subtyping on lifetimes in terms of lifetimes: if `'a : 'b`
292
+
("a outlives b"), then `'a` is a subtype of `b`. This is a
341
293
large source of confusion, because a bigger scope is a *sub type* of a smaller scope.
342
294
This does in fact make sense. The intuitive reason for this is that if you expect an
343
-
`&'a u8`, then it's totally fine for me to hand you an `&'static u8`, in the same way
295
+
`&'a u8`, then it's totally fine for me to hand you an `&'static u8` in the same way
344
296
that if you expect an Animal in Java, it's totally fine for me to hand you a Cat.
345
297
346
298
(Note, the subtyping relationship and typed-ness of lifetimes is a fairly arbitrary
347
299
construct that some disagree with. I just find that it simplifies this analysis.)
348
300
301
+
TODO: higher rank lifetime subtyping
302
+
349
303
Variance is where things get really harsh.
350
304
351
305
Variance is a property that *type constructors* have. A type constructor in Rust
@@ -356,21 +310,27 @@ take a lifetime and a type.
356
310
A type constructor's *variance* is how the subtypes of its inputs affects the
357
311
subtypes of its outputs. There are three kinds of variance:
358
312
359
-
* F is *covariant* if `T <: U` implies `F<T> <: F<U>`
360
-
* F is *contravariant* if `T <: U` implies `F<U> <: F<T>`
313
+
* F is *variant* if `T` being a subtype of `U` implies `F<T>` is a subtype of `F<U>`
361
314
* F is *invariant* otherwise (no subtyping relation can be derived)
362
315
316
+
(For those of you who are familiar with variance from other languages, what we refer
317
+
to as "just" variant is in fact *covariant*. Rust does not have contravariance.
318
+
Historically Rust did have some contravariance but it was scrapped due to poor
319
+
interactions with other features.)
320
+
363
321
Some important variances:
364
322
365
-
*`&` is covariant (as is *const by metaphor)
323
+
*`&` is variant (as is *const by metaphor)
366
324
*`&mut` is invariant (as is *mut by metaphor)
367
-
*`Fn(T)` is contravariant with respect to `T`
368
-
*`Box`, `Vec`, and all other collections are covariant
325
+
*`Fn(T) -> U` is invariant with respect to `T`, but variant with respect to `U`
326
+
*`Box`, `Vec`, and all other collections are variant
369
327
*`UnsafeCell`, `Cell`, `RefCell`, `Mutex` and all "interior mutability"
370
328
types are invariant
371
329
372
330
To understand why these variances are correct and desirable, we will consider several
373
-
examples. We have already covered why `&` should be covariant.
331
+
examples. We have already covered why `&` should be variant when introducing subtyping:
332
+
it's desirable to be able to pass longer-lived things where shorter-lived things are
333
+
needed.
374
334
375
335
To see why `&mut` should be invariant, consider the following code:
The signature of `overwrite` is clearly valid: it takes mutable references to two values
393
353
of the same type, and replaces one with the other. We have seen already that `&` is
394
-
covariant, and `'static` is a subtype of *any*`'a`, so `&'static str` is a
354
+
variant, and `'static` is a subtype of *any*`'a`, so `&'static str` is a
395
355
subtype of `&'a str`. Therefore, if `&mut` was
396
-
*also*covariant, then the lifetime of the `&'static str` would successfully be
356
+
*also*variant, then the lifetime of the `&'static str` would successfully be
397
357
"shrunk" down to the shorter lifetime of the string, and `replace` would be
398
358
called successfully. The string would subsequently be dropped, and `forever_str`
399
359
would point to freed memory when we print it!
400
360
401
-
Therefore `&mut` should be invariant. This is the general theme of covariance vs
402
-
invariance: if covariance would allow you to *store* a short-lived value in a
361
+
Therefore `&mut` should be invariant. This is the general theme of variance vs
362
+
invariance: if variance would allow you to *store* a short-lived value in a
403
363
longer-lived slot, then you must be invariant.
404
364
405
-
`Box` and `Vec` are interesting cases because they're covariant, but you can
365
+
`Box` and `Vec` are interesting cases because they're variant, but you can
406
366
definitely store values in them! This is fine because *you can only store values
407
367
in them through a mutable reference*! The mutable reference makes the whole type
408
368
invariant, and therefore prevents you from getting in trouble.
409
369
410
-
Being covariant allows them to be covariant when shared immutably (so you can pass
370
+
Being variant allows them to be variant when shared immutably (so you can pass
411
371
a `&Box<&'static str>` where a `&Box<&'a str>` is expected). It also allows you to
412
372
forever weaken the type by moving it into a weaker slot. That is, you can do:
413
373
414
374
```rust
415
375
fnget_box<'a>(&'au8) ->Box<&'astr> {
376
+
// string literals are `&'static str`s
416
377
Box::new("hello")
417
378
}
418
379
```
@@ -424,51 +385,67 @@ The variance of the cell types similarly follows. `&` is like an `&mut` for a
424
385
cell, because you can still store values in them through an `&`. Therefore cells
425
386
must be invariant to avoid lifetime smuggling.
426
387
427
-
`Fn` is the most confusing case, largely because contravariance is easily the
428
-
most confusing kind of variance, and basically never comes up. To understand it,
429
-
consider a function `len` that takes a function `F`.
388
+
`Fn` is the most subtle case, because it has mixed variance. To see why
389
+
`Fn(T) -> U` should be invariant over T, consider the following function
390
+
signature:
430
391
431
392
```rust
432
-
fnlen<F>(func:F) ->usize
433
-
whereF:Fn(&'staticstr) ->usize
434
-
{
435
-
func("hello")
436
-
}
393
+
// 'a is derived from some parent scope
394
+
fnfoo(&'astr) ->usize;
437
395
```
438
396
439
-
We require that F is a Fn that can take an `&'static str` and returns a usize. Now
440
-
say we have a function that can take an `&'a str` (for *some*`'a`). Such a function actually
441
-
accepts *more* inputs, since `&'static str` is a subtype of `&'a str`. Therefore
442
-
`len` should happily accept such a function!
397
+
This signature claims that it can handle any &str that lives *at least* as long
398
+
as `'a`. Now if this signature was variant with respect to &str, that would mean
399
+
400
+
```rust
401
+
fnfoo(&'staticstr) ->usize;
402
+
```
443
403
444
-
So a `Fn(&'a str)` is a subtype of a `Fn(&'static str)` because
445
-
`&'static str` is a subtype of `&'a str`. Exactly contravariance.
404
+
could be provided in its place, as it would be a subtype. However this function
405
+
has a *stronger* requirement: it says that it can *only* handle `&'static str`s,
406
+
and nothing else. Therefore functions are not variant over their arguments.
446
407
447
-
The variance of `*const` and `*mut` is basically arbitrary as they're not at all
448
-
type or memory safe, so their variance is determined in analogy to & and &mut
449
-
respectively.
408
+
To see why `Fn(T) -> U` should be *variant* over U, consider the following
409
+
function signature:
410
+
411
+
```rust
412
+
// 'a is derived from some parent scope
413
+
fnfoo(usize) ->&'astr;
414
+
```
415
+
416
+
This signature claims that it will return something that outlives `'a`. It is
417
+
therefore completely reasonable to provide
418
+
419
+
```rust
420
+
fnfoo(usize) ->&'staticstr;
421
+
```
422
+
423
+
in its place. Therefore functions *are* variant over their return type.
424
+
425
+
`*const` has the exact same semantics as &, so variance follows. `*mut` on the
426
+
other hand can dereference to an &mut whether shared or not, so it is marked
427
+
as invariant in analogy to cells.
450
428
451
429
This is all well and good for the types the standard library provides, but
452
-
how is variance determined for type that *you* define? A struct informally
453
-
speaking inherits the variance of its fields. If a struct `Foo`
430
+
how is variance determined for type that *you* define? A struct, informally
431
+
speaking, inherits the variance of its fields. If a struct `Foo`
454
432
has a generic argument `A` that is used in a field `a`, then Foo's variance
455
433
over `A` is exactly `a`'s variance. However this is complicated if `A` is used
456
434
in multiple fields.
457
435
458
-
* If all uses of A are covariant, then Foo is covariant over A
459
-
* If all uses of A are contravariant, then Foo is contravariant over A
436
+
* If all uses of A are variant, then Foo is variant over A
460
437
* Otherwise, Foo is invariant over A
461
438
462
439
```rust
463
440
structFoo<'a, 'b, A, B, C, D, E, F, G, H> {
464
-
a:&'aA, //covariant over 'a and A
441
+
a:&'aA, //variant over 'a and A
465
442
b:&'bmutB, // invariant over 'b and B
466
-
c:*constC, //covariant over C
443
+
c:*constC, //variant over C
467
444
d:*mutD, // invariant over D
468
-
e:Vec<E>, //covariant over E
445
+
e:Vec<E>, //variant over E
469
446
f:Cell<F>, // invariant over F
470
-
g:G//covariant over G
471
-
h1:H// would also be covariant over H except...
447
+
g:G//variant over G
448
+
h1:H// would also be variant over H except...
472
449
h2:Cell<H> // invariant over H, because invariance wins
473
450
}
474
451
```
@@ -497,8 +474,9 @@ correct variance and drop checking.
497
474
498
475
We do this using *PhantomData*, which is a special marker type. PhantomData
499
476
consumes no space, but simulates a field of the given type for the purpose of
500
-
variance. This was deemed to be less error-prone than explicitly telling the
501
-
type-system the kind of variance that you want.
477
+
static analysis. This was deemed to be less error-prone than explicitly telling
478
+
the type-system the kind of variance that you want, while also providing other
479
+
useful information.
502
480
503
481
Iter logically contains `&'a T`, so this is exactly what we tell
504
482
the PhantomData to simulate:
@@ -526,16 +504,16 @@ However the one exception is with PhantomData. Given a struct like Vec:
526
504
527
505
```
528
506
struct Vec<T> {
529
-
data: *const T, // *const for covariance!
507
+
data: *const T, // *const for variance!
530
508
len: usize,
531
509
cap: usize,
532
510
}
533
511
```
534
512
535
-
dropck will generously determine that Vec<T> does not contain any values of
513
+
dropck will generously determine that Vec<T> does not own any values of
536
514
type T. This will unfortunately allow people to construct unsound Drop
537
515
implementations that access data that has already been dropped. In order to
538
-
tell dropck that we *do* own values of type T and may call destructors of that
516
+
tell dropck that we *do* own values of type T, and may call destructors of that
539
517
type, we must add extra PhantomData:
540
518
541
519
```
@@ -700,3 +678,56 @@ Bar is *also* live. Since IterMut is always live when `next` can be called, if
700
678
to it exist!
701
679
702
680
681
+
682
+
683
+
684
+
# Weird Lifetimes
685
+
686
+
Given the following code:
687
+
688
+
```rust
689
+
structFoo;
690
+
691
+
implFoo {
692
+
fnmutate_and_share(&mutself) ->&Self { &*self }
693
+
fnshare(&self) {}
694
+
}
695
+
696
+
fnmain() {
697
+
letmutfoo=Foo;
698
+
letloan=foo.mutate_and_share();
699
+
foo.share();
700
+
}
701
+
```
702
+
703
+
One might expect it to compile. We call `mutate_and_share`, which mutably borrows
704
+
`foo`*temporarily*, but then returns *only* a shared reference. Therefore we
705
+
would expect `foo.share()` to succeed as `foo` shouldn't be mutably borrowed.
706
+
707
+
However when we try to compile it:
708
+
709
+
```text
710
+
<anon>:11:5: 11:8 error: cannot borrow `foo` as immutable because it is also borrowed as mutable
711
+
<anon>:11 foo.share();
712
+
^~~
713
+
<anon>:10:16: 10:19 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
714
+
<anon>:10 let loan = foo.mutate_and_share();
715
+
^~~
716
+
<anon>:12:2: 12:2 note: previous borrow ends here
717
+
<anon>:8 fn main() {
718
+
<anon>:9 let mut foo = Foo;
719
+
<anon>:10 let loan = foo.mutate_and_share();
720
+
<anon>:11 foo.share();
721
+
<anon>:12 }
722
+
^
723
+
```
724
+
725
+
What happened? Well, the lifetime of `loan` is derived from a *mutable* borrow.
726
+
This makes the type system believe that `foo` is mutably borrowed as long as
727
+
`loan` exists, even though it's a shared reference. This isn't a bug, although
728
+
one could argue it is a limitation of the design. In particular, to know if
729
+
the mutable part of the borrow is *really* expired we'd have to peek into
730
+
implementation details of the function. Currently, type-checking a function
731
+
does not need to inspect the bodies of any other functions or types.
0 commit comments