Skip to content

Commit 9551eac

Browse files
committed
Add subtyping
1 parent c434e8e commit 9551eac

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed

dive-into-rust/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod drop_check;
1313
pub mod interior_mut;
1414
pub mod lifetime;
1515
pub mod nll;
16+
pub mod subtyping;
1617
pub mod unsafe_demo;
1718
pub mod variance;
1819

dive-into-rust/src/subtyping.rs

+263
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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

Comments
 (0)