「这是我参与11月更文挑战的第 6 天,活动详情查看:2021最后一次更文挑战」
泛型和trait是Rust类型系统中最重要的两个概念。
泛型并不是Rust特有的概念,在很多强类型编程语言中也支持泛型。泛型允许开发者编写一些在使用时才指定类型的代码。
Rust 中的 trait
,它借鉴了Haskell的Typeclass。同时也是:Rust 实现零成本抽象的基石。
- trait是Rust唯一的接口抽象方式
- 可以静态分发,也可以动态调用
- 可以当作标记类型拥有某些特定行为的"标签"来使用
最关键的一句话:trait 是对类型行为的抽象。
泛型
在泛型的类型签名中,通常使用字母T来代表一个泛型。也就是说这个Option<T>枚举类型对于任何类型都适用。
这样的话,我们就没必要给每个类型都定义一遍Option枚举,比如 Option<u32>或 Option<String>等。标准库提供的 Option<T>
类型已经通过 use std::prelude::v1::*
自动引入了每个Rust包中,所以可以直接使用 Some(T)/None
来表示一个 Option<T>
类型,而不需要写 Option::Some(T)
或 Option::None
。
Trait
pub trait Fly {
fn fly(&self) -> bool;
}
struct Duck;
struct Pig;
// impl block
impl Fly for Duck {
fn fly(&self) -> bool {
return true;
}
}
impl Fly for Pig {
fn fly(&self) -> bool {
return false;
}
}
// use Triat
fn fly_static<T: Fly>(s: T) -> bool {
s.fly()
}
fn fly_dyn(s: &Fly) -> bool {
s.fly()
}
fn main() {
let pig = Pig;
assert_eq!(fly_static::<Pig>(pig), false);
let duck = Duck;
assert_eq!(fly_static::<Duck>(duck), true);
assert_eq!(fly_dyn(&Pig), false);
assert_eq!(fly_dyn(&Duck), true);
}
我们需要关注两个点:
fly_static()
我们可以看到:fly_static<T: Fly>(s: T)
。其中:T:Fly
这种语法形式使用 Fly Trait 对泛型T进行行为上的限定 → 代表实现 Fly Trait 的类型,或者是拥有 fly 这种行为的类型。
这种限制在Rust中被成为:Trait bound。
通过Trait bound,限制了fy_static泛型函数参数的类型范围。如果有不满足该限定的类型传入,编译器就会识别并报错。
fy_static::<Pig>
使用了assert!断言,用于判断 fy_static::<Pig>(pig)
的调用结果是否将会返回 false。其中 ::<Pig>
这样的语法形式用于给泛型函数指定具体的类型,这里调用的是 Pig实现的fly 方法。
上面这种调用方式在 Rust 中叫静态分发。
Rust 编译器会为 fy_static::<Pig>(pig)和 fy_static::<Duck>(duck)这两个具体类型的调用生成特殊化的代码。也就是说,对于编译器来说,这种抽象并不存在,因为在编译阶段,泛型已经被展开为具体类型的代码。