1 泛型范式
C++、JAVA泛型范式有非常广泛的应用,也即模版方法和模版类,我们使用非常熟悉。
模版方法和模版类会在编译期间具化,在rust中叫单态化,将模版结构、方法、trait按照具体的类型单态具化为若干拷贝代码。
- 好处:模版方法和模版类在编译期间,把所有用到的泛型函数的泛型参数展开,生成若干个函数,这样就和普通函数一样,没有任何效率损失。
- 缺点:编译器需要找到所有用到的不同类型,一个个编译,编译速度会减慢 ,编出来的二进制会比较大,因为泛型函数的二进制代码实际存在 N 份。
Rust的是一种静态强类型语言,其多态有参数多态、特型多态、子类型多态。
- 参数多态是指,代码操作的类型是一个满足某些约束的参数,而非具体的类型。
- 特设多态是指同一种行为有多个不同实现的多态。
- 子类型多态是指,在运行时,子类型可以被当成父类型使用。
2. 参数多态和特型多态
参数多态和特型通过结构体、函数、trait三种语法场景表现出来。
2.1 结构体多态
结构体多态是对相同的数据结构行为的抽象,在rust中也比较常见,例如
enum Option<T> {
Some(T),
None,
}
2.2 函数多态
函数多态也即模版方法, 可以直接进行限定,或者用where语句进行限定(限定 英文是Bound(边界)是一个用于指定泛型类型参数的限制条件)
fn print_value<T: std::fmt::Display>(value: T)
fn print_value<T>(value: T) where T: std::fmt::Display
也可以用+进行多重限定
fn print_value<T: Debug + Display>(value: T)
另外利用impl,可以进行参数或者返回值约束(impl的好处是解决限定是组合的情况)(注意看下面的注释处,可以返回Itrerator<item=i32>、Box<Itrerator<item=i32>>,甚至更长的组合,这里我们用impl Itrerator<item=i32>就可以解决问题了)
fn combine_vecs<T: (
v: Vec<i32>,
u: Vec<i32>,
) -> imp Iterator<Item=i32> {
// 注意这里可以返回实现了Itrerator<item=i32>的结构,也可以返回满足Box<Itrerator<item=i32>>的结构
}
2.3 trait多态
一个典型的triat模版如下
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
对于trait,有Self、子类型、关联类型几个特性要特别了解
- trait多态,第一个要理解的是Self,Self也即结构本身,例如Clone的返回值,Self返回的就是实现了CloneTrait的结构本身,而函数中&self实际是 self: &Self的简写,&mut self是self: &mut Self的简写。
pub trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
- trait也有子类型,例如
trait MyTrait : Debug {
}
这里如果结构体实现MyTrait,也必须同时实现Debug的方法,否则编译器会报错。
- trait的关联类型,如下:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
迭代器trait中的Item就是关联类型,实现一个迭代器例子如下:
//(来自标准库中std::env模块的代码)
impl Iterator for Args {
type Item = String;
fn next(&mut self) -> Option<String> {
...
}
...
}
3. trait object
如下例子,&dyn trait或者Box<dyn trait>,实现了子类型多态。
trait Animal {
fn speak(&self);
}
struct Dog;
impl Animal for Dog {
fn speak(&self) {
println!("Woof!");
}
}
struct Cat;
impl Animal for Cat {
fn speak(&self) {
println!("Meow!");
}
}
// 等价于 fn name<T: Animal>(animal: T) -> &'static str;
fn name(animal: impl Animal) -> &'static str {
animal.name()
}
fn main() {
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
];
for animal in animals {
animal.speak();
}
}
类似于C++虚表的原理,rust使用了胖指针实现子类型多态, 例子中将具体的Dog和Cat转为胖指针,每个胖指针都有2个字段,为固定大小。一个字段指向数据结构本身,一个字段指向函数表
trait object
3. 小结
rust泛型包括参数泛型和特型泛型(1)数据结构泛型 2)函数泛型 3)trait泛型),也包括子类型泛型(trait object)。对比C++和Java,都是在类和函数中表现泛型,其原理是想通的,学习的时候要仔细琢磨一下。
网友评论