🙆♀️
0027-trait-object
on_screenの型はdyn Drawになっている
これはgenericsとちがって引数のdrawの型は動的となる。またコンパイルされるコードも型ごとにバイナリが生成されたりしない
実際のところon_screen内のboundsはvtableを参照して解決することになるため実行時間が不利になり
ただしgenericsとちがってコンパイル時に型ごとのbinaryを生成しないためコンパイルタイムは減る
内部的な型情報は失われdraw変数は実体ではなく抽象化され、SquareでもCircleでもtrait_obj::Drawとなる、またこの時にTypeIdも同一のものになる
use std::any::{type_name, TypeId};
#[derive(Debug, Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct Bounds {
top_left: Point,
bottom_right: Point,
}
static SCREEN_BOUNDS:Bounds = Bounds {
top_left: Point { x: 0.0, y: 0.0 },
bottom_right: Point { x: 100.0, y: 100.0 },
};
#[derive(Clone, Debug)]
struct Square {
top_left: Point,
size: f64,
}
pub trait Draw{
fn bounds(&self) -> Bounds;
}
impl Draw for Square {
fn bounds(&self) -> Bounds {
Bounds {
top_left: self.top_left,
bottom_right: Point {
x: self.top_left.x + self.size,
y: self.top_left.y + self.size,
},
}
}
}
#[derive(Clone, Debug)]
struct Circle {
center: Point,
radius: f64,
}
impl Draw for Circle {
fn bounds(&self) -> Bounds {
Bounds {
top_left: Point {
x: self.center.x - self.radius,
y: self.center.y - self.radius,
},
bottom_right: Point {
x: self.center.x + self.radius,
y: self.center.y + self.radius,
},
}
}
}
fn overlap(a: Bounds, b: Bounds) -> Option<Bounds> {
let top_left = Point {
x: a.top_left.x.max(b.top_left.x),
y: a.top_left.y.max(b.top_left.y),
};
let bottom_right = Point {
x: a.bottom_right.x.min(b.bottom_right.x),
y: a.bottom_right.y.min(b.bottom_right.y),
};
if top_left.x < bottom_right.x && top_left.y < bottom_right.y {
Some(Bounds {
top_left,
bottom_right,
})
} else {
None
}
}
pub fn on_screen(draw: &dyn Draw) -> bool {
println!("Type of draw: {:?}", type_name::<&dyn Draw>());
println!("Type of draw: {:?}", TypeId::of::<&dyn Draw>());
overlap(SCREEN_BOUNDS, draw.bounds()).is_some()
}
fn main() {
let square = Square {
top_left: Point { x: 0.0, y: 0.0 },
size: 10.0,
};
on_screen(&square);
let circle = Circle {
center: Point { x: 5.0, y: 5.0 },
radius: 5.0,
};
on_screen(&circle);
}
Discussion