|
| 1 | +#![allow(unused)] |
| 2 | + |
| 3 | +// https://www.youtube.com/watch?v=fI4RG_uq-WU |
| 4 | +// http://pnkfx.org/presentations/rustfest-berlin-2016/slides.html |
| 5 | + |
| 6 | +/// (子类型可以隐性的转换为父类型) |
| 7 | +/// Int <: Real => |
| 8 | +/// Real -> Int <: Int -> Real (参数逆变, 返回类型协变) |
| 9 | +
|
| 10 | +/* |
| 11 | +
|
| 12 | +Y <: X |
| 13 | +A -> Y <: A -> X |
| 14 | +
|
| 15 | +aka -> is covariant with respect to its return type. |
| 16 | +
|
| 17 | +Example instance: |
| 18 | +
|
| 19 | +Int <: Real |
| 20 | +Real -> Int <: Real -> Real |
| 21 | +All caller can do is get an X from calling the function; |
| 22 | +So it is safe to narrow and use a Y as the return value. |
| 23 | +
|
| 24 | +
|
| 25 | +
|
| 26 | +
|
| 27 | +"Have a function of type Real -> Int, want one like Int -> Int" |
| 28 | +
|
| 29 | +Sometimes unintuitive |
| 30 | +
|
| 31 | +"Client says they will only feed integer values into the function, so it is safe to provide something that can consume any real number." |
| 32 | +
|
| 33 | +
|
| 34 | +
|
| 35 | +
|
| 36 | +Y <: X B <: A |
| 37 | +A -> Y <: B -> X |
| 38 | +
|
| 39 | +aka -> is covariant with respect to its return type, and -> is contravariant with respect to its argument type. |
| 40 | +
|
| 41 | +All caller can do is feed in more specific B (and get out more general X). |
| 42 | +So it is safe to be more liberal and accept any A at all, and guarantee the more specific Y as return value. |
| 43 | +
|
| 44 | +*/ |
| 45 | + |
| 46 | +// *** Cell example ******************************************************* |
| 47 | + |
| 48 | +struct MyCell<T> { |
| 49 | + value: T, |
| 50 | +} |
| 51 | + |
| 52 | +impl<T: Copy> MyCell<T> { |
| 53 | + fn new(x: T) -> MyCell<T> { |
| 54 | + MyCell { value: x } |
| 55 | + } |
| 56 | + fn get(&self) -> T { |
| 57 | + self.value |
| 58 | + } |
| 59 | + fn set(&self, value: T) { |
| 60 | + use std::ptr; |
| 61 | + unsafe { |
| 62 | + ptr::write(&self.value as *const _ as *mut _, value); |
| 63 | + } |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +static X: i32 = 10; |
| 68 | + |
| 69 | +fn step1<'a>(r_c1: &MyCell<&'a i32>) { |
| 70 | + let val: i32 = 13; |
| 71 | + step2(&val, r_c1); |
| 72 | + println!("step1 value: {}", r_c1.value); |
| 73 | +} |
| 74 | + |
| 75 | +fn step2<'b>(r_val: &'b i32, r_c2: &MyCell<&'b i32>) { |
| 76 | + r_c2.set(r_val); |
| 77 | +} |
| 78 | + |
| 79 | +#[test] |
| 80 | +fn test_mycell_short_lifetime() { |
| 81 | + let cell = MyCell::new(&X); |
| 82 | + step1(&cell); |
| 83 | + println!(" end value: {}", cell.value); |
| 84 | + |
| 85 | + // assert!(false); |
| 86 | +} |
| 87 | + |
| 88 | +/* |
| 89 | +mod test_stdcell_short_lifetime { |
| 90 | + use std::cell::Cell; |
| 91 | + static X: i32 = 10; |
| 92 | +
|
| 93 | + #[test] |
| 94 | + fn test_stdcell_short_lifetime() { |
| 95 | + let cell = Cell::new(&X); |
| 96 | + step1(&cell); |
| 97 | + } |
| 98 | + fn step1<'a>(r_c1: &Cell<&'a i32>) { |
| 99 | + let val: i32 = 13; |
| 100 | + // error[E0597]: `val` does not live long enough |
| 101 | + step2(&val, r_c1); |
| 102 | + } |
| 103 | + fn step2<'b>(r_val: &'b i32, r_c2: &Cell<&'b i32>) { |
| 104 | + r_c2.set(r_val); |
| 105 | + } |
| 106 | +} |
| 107 | +
|
| 108 | +
|
| 109 | +*/ |
| 110 | + |
| 111 | +/* |
| 112 | +
|
| 113 | +struct MyCell<T> { |
| 114 | + value: T, |
| 115 | +} |
| 116 | +
|
| 117 | +pub struct Cell<T> { |
| 118 | + value: UnsafeCell<T>, |
| 119 | +} |
| 120 | +
|
| 121 | +#[lang = "unsafe_cell"] |
| 122 | +pub struct UnsafeCell<T: ?Sized> { |
| 123 | + value: T, |
| 124 | +} |
| 125 | +
|
| 126 | +*/ |
| 127 | + |
| 128 | +// *** lifetime *********************************************************** |
| 129 | + |
| 130 | +/// Picks either `x` or `y`, based on some internal choice. |
| 131 | +fn pick<'a>(x: &'a i32, y: &'static i32) -> &'a i32 { |
| 132 | + if *x > 0 { |
| 133 | + x |
| 134 | + } else { |
| 135 | + y |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +static GLOBAL: i32 = 100; |
| 140 | + |
| 141 | +#[test] |
| 142 | +fn pick_test() { |
| 143 | + let temp: i32 = 200; |
| 144 | + pick(&temp, &GLOBAL); |
| 145 | +} |
| 146 | + |
| 147 | +// fn promote<'a>(x: &'a i32) -> &'static i32 { |
| 148 | +// return x; |
| 149 | +// } |
| 150 | + |
| 151 | +// #[test] |
| 152 | +// fn promote_test() { |
| 153 | +// let temp: i32 = 200; |
| 154 | +// promote(&temp); |
| 155 | +// } |
| 156 | + |
| 157 | +/* |
| 158 | +
|
| 159 | +Insight: For any type T and any lifetime 'a, clearly &'static T should be valid anywhere that &'a T is. |
| 160 | +
|
| 161 | +'static outlives 'a |
| 162 | +&'static T <: &'a T |
| 163 | +
|
| 164 | +
|
| 165 | +
|
| 166 | +For any type T and lifetimes 'a, 'b, if 'b outlives 'a, |
| 167 | +then &'b T should be valid anywhere &'a T is. |
| 168 | +
|
| 169 | +'b outlives 'a |
| 170 | +&'b T <: &'a T |
| 171 | +
|
| 172 | +
|
| 173 | +
|
| 174 | +Already established we should have &'static T <: &'a T. |
| 175 | +What about a reference to a reference? |
| 176 | +Should &'a &'static T <: &'a &'a T ...? |
| 177 | +Intuition: All you can do with a &X is read data from the X it holds. |
| 178 | +Analogous to a function A -> Y <: A -> X |
| 179 | +'b outlives 'a |
| 180 | +Y <: X |
| 181 | +&'b Y <: &'a X |
| 182 | +&X is covariant with respect to X |
| 183 | +
|
| 184 | +
|
| 185 | +
|
| 186 | +But that's &X; what about &mut X? --- Invariance |
| 187 | +
|
| 188 | +Insight: for any type X and any lifetime 'a, |
| 189 | +&'static mut X is valid anywhere &'a mut X is. |
| 190 | +But we cannot generalize to Y <: X |
| 191 | +
|
| 192 | +Intuition: Once you allow mutation through a reference, |
| 193 | +the type itself must remain fixed. |
| 194 | +
|
| 195 | +*/ |
| 196 | + |
| 197 | +// *** function type variant ********************************************** |
| 198 | + |
| 199 | +mod demo_variance_and_static_ref { |
| 200 | + fn provide(m: &'static i32) { |
| 201 | + let val = 13; |
| 202 | + expect(&val, m); |
| 203 | + } |
| 204 | + fn expect<'a>(_: &'a i32, _r: &'a i32) { |
| 205 | + unimplemented!() |
| 206 | + } |
| 207 | +} |
| 208 | + |
| 209 | +mod demo_variance_and_static_ref_hof { |
| 210 | + fn prov_hof(f: fn(&usize) -> &'static i32) { |
| 211 | + let val = 13; |
| 212 | + exp_hof(&val, f); |
| 213 | + } |
| 214 | + fn exp_hof<'a>(_: &'a i32, _f: fn(&'a usize) -> &'a i32) { |
| 215 | + unimplemented!() |
| 216 | + } |
| 217 | +} |
| 218 | +// Functions even contravariant with respect to their argument types |
| 219 | + |
| 220 | +/* |
| 221 | +
|
| 222 | +Where does variance come from? |
| 223 | +Compiler deduces the variance of a type (with respect to its type parameters) based on the structure of that type. |
| 224 | +
|
| 225 | +struct OuterInline<T> { one: T, inner: InnerInline<T> } |
| 226 | +struct InnerInline<T> { data: T } |
| 227 | +InnerInline and OuterInline both covariant with respect to T |
| 228 | +
|
| 229 | +struct OuterRef<'a, T: 'a> { one: &'a mut T, inner: InnerRef<'a, T> } |
| 230 | +struct InnerRef<'a, T: 'a> { data: &'a T } |
| 231 | +InnerRef is covariant w.r.t. T, while OuterRef is invariant w.r.t. T |
| 232 | +
|
| 233 | +If compiler sees a PhantomData<SomeType>, it traverses the structure of SomeType as if it were embedded directly. |
| 234 | +
|
| 235 | +
|
| 236 | +
|
| 237 | +What's up with MyCell |
| 238 | +Structural definition of MyCell alone implies it is covariant w.r.t. T |
| 239 | +
|
| 240 | +This (broken) method violates rules associated with covariance: |
| 241 | +```rust |
| 242 | + fn set(&self, value: T) { |
| 243 | + use std::ptr; |
| 244 | + unsafe { |
| 245 | + ptr::write(&self.value as *const _ as *mut _, value); |
| 246 | + } |
| 247 | + } |
| 248 | +``` |
| 249 | +Must impose invariance |
| 250 | +
|
| 251 | +Use PhantomData<fn (T) -> T> in MyCell<T>: one way |
| 252 | +
|
| 253 | +Use UnsafeCell<T> in MyCell<T>: better way |
| 254 | +
|
| 255 | +
|
| 256 | +
|
| 257 | +Pop Quiz |
| 258 | +&'static mut T <: &'a mut T |
| 259 | +&'a &'static mut T <: &'a &'a mut T |
| 260 | +&'a mut &'static T <: &'a mut &'a T |
| 261 | +In case you care: Yes, Yes, No |
| 262 | +
|
| 263 | +*/ |
0 commit comments