Rust Lang Book Ch.17 OOP
继承/多态性
书中认为继承给子类分享了一些可能并不需要的代码,因此并不好。Rust中最接近继承/多态的是trait。
通过使用不同的Trait和Trait bound,可以达到类似接口的效果。我们可以要求一些参数/域实现了某种Trait而无需知道究竟这些参数/域是什么具体类型,这样的参数/域称之为Trait Object。一般来说,Trait Object需要使用某些pointer,再搭配dyn关键字(即dynamically Sized type说明该trait object所占空间大小在运行时才能确定)然后再说明相关的trait。
Rust会保证传入Trait Object位置的必然已经实现了对应的Trait。
当使用Trait Object时,Rust使用dynamic dispatch,即动态分配,在运行的时候决定具体的需要使用的方法。dynamic dispatch会带来一定的运行开销,同时无法inline,或者说unrolling一些代码,但是可以带来更灵活的语法。
pub trait Draw {
fn draw(&self);
}
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
impl Screen {
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
pub struct Screen<T: Draw> {
pub components: Vec<T>,
}
impl<T> Screen<T>
where
T: Draw,
{
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
pub struct Button {
pub width: u32,
pub height: u32,
pub label: String,
}
impl Draw for Button {
fn draw(&self) {
// code to actually draw a button
}
}
use gui::{Button, Screen};
fn main() {
let screen = Screen {
components: vec![
Box::new(SelectBox {
width: 75,
height: 10,
options: vec![
String::from("Yes"),
String::from("Maybe"),
String::from("No"),
],
}),
Box::new(Button {
width: 50,
height: 10,
label: String::from("OK"),
}),
//此时两种type可以混用!
],
};
screen.run();
}
不过,这些Trait Bound所对应的Trait Object需要Object Safety,具体来说是:
1. 返回类型不能是Self。
2. 没有generic type parameters。
这些都是因为Rust使用trait object时不知道传入的trait object的具体类型,如果返回Self或者带有泛型,那就无法追踪变量类型了。
例如,带有返回Self方法clone的trait Clone就不能够使用。
pub struct Screen {
pub components: Vec<Box<dyn Clone>>,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` cannot be made into an object
}

浙公网安备 33010602011771号