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
}