@@ -42,11 +42,11 @@ fn debug<T: std::fmt::Debug>(a: T, b: T) {
42
42
}
43
43
44
44
fn main () {
45
- let a : & 'static str = " hello" ;
45
+ let hello : & 'static str = " hello" ;
46
46
{
47
- let b = String :: from (" world" );
48
- let b = & b ; // 'b has a shorter lifetime than 'static
49
- debug (a , b );
47
+ let world = String :: from (" world" );
48
+ let world = & world ; // 'b has a shorter lifetime than 'static
49
+ debug (hello , world );
50
50
}
51
51
}
52
52
```
@@ -58,10 +58,10 @@ we might see the following error:
58
58
error[E0308]: mismatched types
59
59
--> src/main.rs:10:16
60
60
|
61
- 10 | debug(a, b );
62
- | ^
63
- | |
64
- | expected `&'static str`, found struct `&'b str`
61
+ 10 | debug(hello, world );
62
+ | ^
63
+ | |
64
+ | expected `&'static str`, found struct `&'b str`
65
65
```
66
66
67
67
This is over-restrictive. In this case, what we want is to accept any type that lives * at least as long* as ` 'b ` .
@@ -83,11 +83,11 @@ fn debug<T: std::fmt::Debug>(a: T, b: T) {
83
83
}
84
84
85
85
fn main () {
86
- let a : & 'static str = " hello" ;
86
+ let hello : & 'static str = " hello" ;
87
87
{
88
- let b = String :: from (" world" );
89
- let b = & b ; // 'b has a shorter lifetime than 'static
90
- debug (a , b ); // a silently converts from `&'static str` into `&'b str`
88
+ let world = String :: from (" world" );
89
+ let world = & world ; // 'b has a shorter lifetime than 'static
90
+ debug (hello , world ); // a silently converts from `&'static str` into `&'b str`
91
91
}
92
92
}
93
93
```
@@ -98,26 +98,25 @@ Above, we glossed over the fact that `'static: 'b` implied that `&'static T: &'b
98
98
It's not always as simple as this example though, to understand that let's try extend this example a bit
99
99
100
100
``` rust,compile_fail
101
- fn debug <T>(a : &mut T, b : T) {
102
- *a = b ;
101
+ fn assign <T>(input : &mut T, val : T) {
102
+ *input = val ;
103
103
}
104
104
105
105
fn main() {
106
- let mut a : &'static str = "hello";
106
+ let mut hello : &'static str = "hello";
107
107
{
108
- let b = String::from("world");
109
- let b = &b;
110
- debug(&mut a, b);
108
+ let world = String::from("world");
109
+ assign(&mut hello, &world);
111
110
}
112
111
}
113
112
```
114
113
115
- This has a memory bug in it .
114
+ If this were to compile, this would have a memory bug.
116
115
117
116
If we were to expand this out, we'd see that we're trying to assign a ` &'b str ` into a ` &'static str ` ,
118
117
but the problem is that as soon as ` b ` goes out of scope, ` a ` is now invalid, even though it's supposed to have a ` 'static ` lifetime.
119
118
120
- However, the implementation of ` debug ` is valid.
119
+ However, the implementation of ` assign ` is valid.
121
120
Therefore, this must mean that ` &mut &'static str ` should ** not** a * subtype* of ` &mut &'b str ` ,
122
121
even if ` 'static ` is a subtype of ` 'b ` .
123
122
@@ -149,21 +148,21 @@ Here is a table of some other type constructors and their variances:
149
148
150
149
| | | 'a | T | U |
151
150
| ---| -----------------| :---------:| :-----------------:| :---------:|
152
- | * | ` &'a T ` | covariant | covariant | |
153
- | * | ` &'a mut T ` | covariant | invariant | |
154
- | * | ` Box<T> ` | | covariant | |
151
+ | | ` &'a T ` | covariant | covariant | |
152
+ | | ` &'a mut T ` | covariant | invariant | |
153
+ | | ` Box<T> ` | | covariant | |
155
154
| | ` Vec<T> ` | | covariant | |
156
- | * | ` UnsafeCell<T> ` | | invariant | |
155
+ | | ` UnsafeCell<T> ` | | invariant | |
157
156
| | ` Cell<T> ` | | invariant | |
158
- | * | ` fn(T) -> U ` | | ** contra** variant | covariant |
157
+ | | ` fn(T) -> U ` | | ** contra** variant | covariant |
159
158
| | ` *const T ` | | covariant | |
160
159
| | ` *mut T ` | | invariant | |
161
160
162
- The types with \* 's are the ones we will be focusing on, as they are in
163
- some sense "fundamental". All the others can be understood by analogy to the others:
161
+ Some of these can be explained simply in relation to the others:
164
162
165
163
* ` Vec<T> ` and all other owning pointers and collections follow the same logic as ` Box<T> `
166
164
* ` Cell<T> ` and all other interior mutability types follow the same logic as ` UnsafeCell<T> `
165
+ * ` UnsafeCell<T> ` having interior mutability gives it the same variance properties as ` &mut T `
167
166
* ` *const T ` follows the logic of ` &T `
168
167
* ` *mut T ` follows the logic of ` &mut T ` (or ` UnsafeCell<T> ` )
169
168
@@ -177,8 +176,72 @@ For more types, see the ["Variance" section][variance-table] on the reference.
177
176
> take references with specific lifetimes (as opposed to the usual "any lifetime",
178
177
> which gets into higher rank lifetimes, which work independently of subtyping).
179
178
180
- Ok, that's enough type theory! Let's try to apply the concept of variance to Rust
181
- and look at some examples.
179
+ Now that we have some more formal understanding of variance,
180
+ let's go through some more examples in more detail.
181
+
182
+ ``` rust,compile_fail
183
+ fn assign<T>(input: &mut T, val: T) {
184
+ *input = val;
185
+ }
186
+
187
+ fn main() {
188
+ let mut hello: &'static str = "hello";
189
+ {
190
+ let world = String::from("world");
191
+ assign(&mut hello, &world);
192
+ }
193
+ }
194
+ ```
195
+
196
+ And what do we get when we run this?
197
+
198
+ ``` text
199
+ error[E0597]: `world` does not live long enough
200
+ --> src/main.rs:9:28
201
+ |
202
+ 6 | let mut hello: &'static str = "hello";
203
+ | ------------ type annotation requires that `world` is borrowed for `'static`
204
+ ...
205
+ 9 | assign(&mut hello, &world);
206
+ | ^^^^^^ borrowed value does not live long enough
207
+ 10 | }
208
+ | - `world` dropped here while still borrowed
209
+ ```
210
+
211
+ Good, it doesn't compile! Let's break down what's happening here in detail.
212
+
213
+ First let's look at the ` assign ` function:
214
+
215
+ ``` rust
216
+ fn assign <T >(input : & mut T , val : T ) {
217
+ * input = val ;
218
+ }
219
+ ```
220
+
221
+ All it does is take a mutable reference and a value and overwrite the referent with it.
222
+ What's important about this function is that it creates a type equality constraint. It
223
+ clearly says in its signature the referent and the value must be the * exact same* type.
224
+
225
+ Meanwhile, in the caller we pass in ` &mut &'static str ` and ` &'spike_str str ` .
226
+
227
+ Because ` &mut T ` is invariant over ` T ` , the compiler concludes it can't apply any subtyping
228
+ to the first argument, and so ` T ` must be exactly ` &'static str ` .
229
+
230
+ This is counter to the ` &T ` case
231
+
232
+ ``` rust
233
+ fn debug <T : std :: fmt :: Debug >(a : T , b : T ) {
234
+ println! (" a = {:?} b = {:?}" , a , b );
235
+ }
236
+ ```
237
+
238
+ Where similarly ` a ` and ` b ` must have the same type ` T ` .
239
+ But since ` &'a T ` * is* covariant over ` 'a ` , we are allowed to perform subtyping.
240
+ So the compiler decides that ` &'static str ` can become ` &'b str ` if and only if
241
+ ` &'static str ` is a subtype of ` &'b str ` , which will hold if ` 'static: 'b ` .
242
+ This is true, so the compiler is happy to continue compiling this code.
243
+
244
+ ---
182
245
183
246
First off, let's revisit the meowing dog example:
184
247
@@ -249,56 +312,6 @@ enough into the place expecting something long-lived.
249
312
250
313
Here it is:
251
314
252
- ``` rust,compile_fail
253
- fn evil_feeder<T>(input: &mut T, val: T) {
254
- *input = val;
255
- }
256
-
257
- fn main() {
258
- let mut mr_snuggles: &'static str = "meow! :3"; // mr. snuggles forever!!
259
- {
260
- let spike = String::from("bark! >:V");
261
- let spike_str: &str = &spike; // Only lives for the block
262
- evil_feeder(&mut mr_snuggles, spike_str); // EVIL!
263
- }
264
- println!("{}", mr_snuggles); // Use after free?
265
- }
266
- ```
267
-
268
- And what do we get when we run this?
269
-
270
- ``` text
271
- error[E0597]: `spike` does not live long enough
272
- --> src/main.rs:9:31
273
- |
274
- 6 | let mut mr_snuggles: &'static str = "meow! :3"; // mr. snuggles forever!!
275
- | ------------ type annotation requires that `spike` is borrowed for `'static`
276
- ...
277
- 9 | let spike_str: &str = &spike; // Only lives for the block
278
- | ^^^^^^ borrowed value does not live long enough
279
- 10 | evil_feeder(&mut mr_snuggles, spike_str); // EVIL!
280
- 11 | }
281
- | - `spike` dropped here while still borrowed
282
- ```
283
-
284
- Good, it doesn't compile! Let's break down what's happening here in detail.
285
-
286
- First let's look at the new ` evil_feeder ` function:
287
-
288
- ``` rust
289
- fn evil_feeder <T >(input : & mut T , val : T ) {
290
- * input = val ;
291
- }
292
- ```
293
-
294
- All it does is take a mutable reference and a value and overwrite the referent with it.
295
- What's important about this function is that it creates a type equality constraint. It
296
- clearly says in its signature the referent and the value must be the * exact same* type.
297
-
298
- Meanwhile, in the caller we pass in ` &mut &'static str ` and ` &'spike_str str ` .
299
-
300
- Because ` &mut T ` is invariant over ` T ` , the compiler concludes it can't apply any subtyping
301
- to the first argument, and so ` T ` must be exactly ` &'static str ` .
302
315
303
316
The other argument is only an ` &'a str ` , which * is* covariant over ` 'a ` . So the compiler
304
317
adopts a constraint: ` &'spike_str str ` must be a subtype of ` &'static str ` (inclusive),
0 commit comments