本文节选自<<Rust语言圣经>>一书
欢迎大家加入Rust编程学院,一起学习交流:
QQ群:1009730433
特征对象
在上一节中有一段代码无法通过编译:
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
Post {
// ...
}
} else {
Weibo {
// ...
}
}
}
其中Post
和Weibo
都实现了Summary
特征,因此上面的函数试图通过返回impl Summary
来返回这两个类型,但是编译器无情的报错了,原因是
impl Trait
的返回值类型并不支持多种不同的类型返回,那如果我们想返回多种类型,该怎么办?
再来考虑一个问题: 现在在做一款游戏,需要将多个对象渲染在屏幕上,这些对象拥有不同的类型,存储在列表中,渲染的时候,循环该列表顺序渲染每个对象即可,在Rust中该怎么实现?
聪明的同学可能已经能想到一个办法,利用枚举:
#[derive(Debug)]
enum UiObject {
Button,
SelectBox,
}
fn main() {
let objects = [
UiObject::Button,
UiObject::SelectBox
];
for o in objects {
draw(o)
}
}
fn draw(o: UiObject) {
println!("{:?}",o);
}
Bingo,这个确实是一个办法,但是问题来了,如果你的对象集合并不能明确知道呢?或者别人想要实现一个UI组件呢?是不是还要修改你的代码增加一个枚举成员?
总之,在编写这个UI库时,我们无法知道所有的UI对象类型,只知道的是:
- UI对象的类型不同
- 需要一个统一的类型来处理这些对象,无论是作为函数参数还是作为列表中的一员
- 需要对每一个对象调用
draw
方法
在拥有继承的语言中,可以定义一个名为 Component
的类,该类上有一个 draw
方法。其他的类比如 Button
、Image
和 SelectBox
会从 Component
派生并因此继承 draw
方法。它们各自都可以覆盖 draw
方法来定义自己的行为,但是框架会把所有这些类型当作是 Component
的实例,并在其上调用 draw
。不过 Rust 并没有继承,我们得另寻出路。
特征对象定义
为了解决上面的所有问题,Rust引入了一个概念 - 特征对象。
在介绍特征对象之前,先来为之前的UI组件定义一个特征:
pub trait Draw {
fn draw(&self);
}
只要组件实现了Draw
特征,就可以调用draw
方法来进行渲染。假设有一个Button
和SelectBox
组件实现了Draw
特征:
pub struct Button {
pub width: u32,
pub height: u32,
pub label: String,
}
impl Draw for Button {
fn