@@ -241,171 +241,50 @@ So the compiler decides that `&'static str` can become `&'b str` if and only if
241
241
` &'static str ` is a subtype of ` &'b str ` , which will hold if ` 'static: 'b ` .
242
242
This is true, so the compiler is happy to continue compiling this code.
243
243
244
- ---
245
-
246
- First off, let's revisit the meowing dog example:
247
-
248
- <!-- ignore: simplified code -->
249
- ``` rust,ignore
250
- fn evil_feeder(pet: &mut Animal) {
251
- let spike: Dog = ...;
252
-
253
- // `pet` is an Animal, and Dog is a subtype of Animal,
254
- // so this should be fine, right..?
255
- *pet = spike;
256
- }
257
-
258
- fn main() {
259
- let mut mr_snuggles: Cat = ...;
260
- evil_feeder(&mut mr_snuggles); // Replaces mr_snuggles with a Dog
261
- mr_snuggles.meow(); // OH NO, MEOWING DOG!
262
- }
263
- ```
264
-
265
- If we look at our table of variances, we see that ` &mut T ` is * invariant* over ` T ` .
266
- As it turns out, this completely fixes the issue! With invariance, the fact that
267
- Cat is a subtype of Animal doesn't matter; ` &mut Cat ` still won't be a subtype of
268
- ` &mut Animal ` . The static type checker will then correctly stop us from passing
269
- a Cat into ` evil_feeder ` .
270
-
271
- The soundness of subtyping is based on the idea that it's ok to forget unnecessary
272
- details. But with references, there's always someone that remembers those details:
273
- the value being referenced. That value expects those details to keep being true,
274
- and may behave incorrectly if its expectations are violated.
275
-
276
- The problem with making ` &mut T ` covariant over ` T ` is that it gives us the power
277
- to modify the original value * when we don't remember all of its constraints* .
278
- And so, we can make someone have a Dog when they're certain they still have a Cat.
279
-
280
- With that established, we can easily see why ` &T ` being covariant over ` T ` * is*
281
- sound: it doesn't let you modify the value, only look at it. Without any way to
282
- mutate, there's no way for us to mess with any details. We can also see why
283
- ` UnsafeCell ` and all the other interior mutability types must be invariant: they
284
- make ` &T ` work like ` &mut T ` !
285
-
286
- Now what about the lifetime on references? Why is it ok for both kinds of references
287
- to be covariant over their lifetimes? Well, here's a two-pronged argument:
288
-
289
- First and foremost, subtyping references based on their lifetimes is * the entire point
290
- of subtyping in Rust* . The only reason we have subtyping is so we can pass
291
- long-lived things where short-lived things are expected. So it better work!
292
-
293
- Second, and more seriously, lifetimes are only a part of the reference itself. The
294
- type of the referent is shared knowledge, which is why adjusting that type in only
295
- one place (the reference) can lead to issues. But if you shrink down a reference's
296
- lifetime when you hand it to someone, that lifetime information isn't shared in
297
- any way. There are now two independent references with independent lifetimes.
298
- There's no way to mess with the original reference's lifetime using the other one.
299
-
300
- Or rather, the only way to mess with someone's lifetime is to build a meowing dog.
301
- But as soon as you try to build a meowing dog, the lifetime should be wrapped up
302
- in an invariant type, preventing the lifetime from being shrunk. To understand this
303
- better, let's port the meowing dog problem over to real Rust.
304
-
305
- In the meowing dog problem we take a subtype (Cat), convert it into a supertype
306
- (Animal), and then use that fact to overwrite the subtype with a value that satisfies
307
- the constraints of the supertype but not the subtype (Dog).
308
-
309
- So with lifetimes, we want to take a long-lived thing, convert it into a
310
- short-lived thing, and then use that to write something that doesn't live long
311
- enough into the place expecting something long-lived.
312
-
313
- Here it is:
314
-
315
-
316
- The other argument is only an ` &'a str ` , which * is* covariant over ` 'a ` . So the compiler
317
- adopts a constraint: ` &'spike_str str ` must be a subtype of ` &'static str ` (inclusive),
318
- which in turn implies ` 'spike_str ` must be a subtype of ` 'static ` (inclusive). Which is to say,
319
- ` 'spike_str ` must contain ` 'static ` . But only one thing contains ` 'static ` -- ` 'static ` itself!
320
-
321
- This is why we get an error when we try to assign ` &spike ` to ` spike_str ` . The
322
- compiler has worked backwards to conclude ` spike_str ` must live forever, and ` &spike `
323
- simply can't live that long.
324
-
325
- So even though references are covariant over their lifetimes, they "inherit" invariance
326
- whenever they're put into a context that could do something bad with that. In this case,
327
- we inherited invariance as soon as we put our reference inside an ` &mut T ` .
328
-
329
- As it turns out, the argument for why it's ok for Box (and Vec, Hashmap, etc.) to
330
- be covariant is pretty similar to the argument for why it's ok for
331
- references to be covariant: as soon as you try to stuff them in something like a
332
- mutable reference, they inherit invariance and you're prevented from doing anything
333
- bad.
334
-
335
- However, Box makes it easier to focus on the by-value aspect of references that we
336
- partially glossed over.
337
-
338
- Unlike a lot of languages which allow values to be freely aliased at all times,
339
- Rust has a very strict rule: if you're allowed to mutate or move a value, you
340
- are guaranteed to be the only one with access to it.
341
-
342
- Consider the following code:
343
-
344
- <!-- ignore: simplified code -->
345
- ``` rust,ignore
346
- let mr_snuggles: Box<Cat> = ..;
347
- let spike: Box<Dog> = ..;
348
-
349
- let mut pet: Box<Animal>;
350
- pet = mr_snuggles;
351
- pet = spike;
352
- ```
353
-
354
- There is no problem at all with the fact that we have forgotten that ` mr_snuggles ` was a Cat,
355
- or that we overwrote him with a Dog, because as soon as we moved mr_snuggles to a variable
356
- that only knew he was an Animal, ** we destroyed the only thing in the universe that
357
- remembered he was a Cat** !
358
-
359
- In contrast to the argument about immutable references being soundly covariant because they
360
- don't let you change anything, owned values can be covariant because they make you
361
- change * everything* . There is no connection between old locations and new locations.
362
- Applying by-value subtyping is an irreversible act of knowledge destruction, and
363
- without any memory of how things used to be, no one can be tricked into acting on
364
- that old information!
244
+ ` Box<T> ` is also * covariant* over ` T ` . This would make sense, since it's supposed to be
245
+ usable the same as ` &T ` . If you try to mutate the box, you'll need a ` &mut Box<T> ` and the
246
+ invariance of ` &mut ` will kick in here.
365
247
366
248
Only one thing left to explain: function pointers.
367
249
368
250
To see why ` fn(T) -> U ` should be covariant over ` U ` , consider the following signature:
369
251
370
252
<!-- ignore: simplified code -->
371
253
``` rust,ignore
372
- fn get_animal () -> Animal ;
254
+ fn get_str () -> &'a str ;
373
255
```
374
256
375
- This function claims to produce an Animal . As such, it is perfectly valid to
257
+ This function claims to produce a ` str ` bound by some liftime ` 'a ` . As such, it is perfectly valid to
376
258
provide a function with the following signature instead:
377
259
378
260
<!-- ignore: simplified code -->
379
261
``` rust,ignore
380
- fn get_animal () -> Cat ;
262
+ fn get_static () -> &'static str ;
381
263
```
382
264
383
- After all, Cats are Animals, so always producing a Cat is a perfectly valid way
384
- to produce Animals. Or to relate it back to real Rust: if we need a function
385
- that is supposed to produce something that lives for ` 'short ` , it's perfectly
386
- fine for it to produce something that lives for ` 'long ` . We don't care, we can
387
- just forget that fact.
265
+ So when the function is called, all it's expecting is a ` &str ` which lives at least the lifetime of ` 'a ` ,
266
+ it doesn't matter if the value actually lives longer.
388
267
389
268
However, the same logic does not apply to * arguments* . Consider trying to satisfy:
390
269
391
270
<!-- ignore: simplified code -->
392
271
``` rust,ignore
393
- fn handle_animal(Animal );
272
+ fn store_ref(&'a str );
394
273
```
395
274
396
275
with:
397
276
398
277
<!-- ignore: simplified code -->
399
278
``` rust,ignore
400
- fn handle_animal(Cat );
279
+ fn store_static(&'static str );
401
280
```
402
281
403
- The first function can accept Dogs, but the second function absolutely can't.
282
+ The first function can accept any string reference as long as it lives at least for ` 'a ` ,
283
+ but the second cannot accept a string reference that lives for any duration less than ` 'static ` ,
284
+ which would cause a conflict.
404
285
Covariance doesn't work here. But if we flip it around, it actually * does*
405
- work! If we need a function that can handle Cats, a function that can handle * any*
406
- Animal will surely work fine. Or to relate it back to real Rust: if we need a
407
- function that can handle anything that lives for at least ` 'long ` , it's perfectly
408
- fine for it to be able to handle anything that lives for at least ` 'short ` .
286
+ work! If we need a function that can handle ` &'static str ` , a function that can handle * any* reference lifetime
287
+ will surely work fine.
409
288
410
289
And that's why function types, unlike anything else in the language, are
411
290
** contra** variant over their arguments.
0 commit comments