特性
特性与接口
为了描述类型可以实现的抽象接口 (abstract interface), Rust引入了特性 (trait) 来定义函数类型签名 (function type signature):
trait HasArea {
fn area(&self) -> f64;
}
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl HasArea for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct Square {
x: f64,
y: f64,
side: f64,
}
impl HasArea for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn print_area<T: HasArea>(shape: T) {
println!("This shape has an area of {}", shape.area());
}其中函数print_area()中的泛型参数T被添加了一个名为HasArea的特性约束 (trait constraint), 用以确保任何实现了HasArea的类型将拥有一个.area()方法。 如果需要多个特性限定 (multiple trait bounds),可以使用+:
其中第二个例子使用了更灵活的where从句,它还允许限定的左侧可以是任意类型, 而不仅仅是类型参数。
定义在特性中的方法称为默认方法 (default method),可以被该特性的实现覆盖。 此外,特性之间也可以存在继承 (inheritance):
如果两个不同特性的方法具有相同的名称,可以使用通用函数调用语法 (universal function call syntax):
关于实现特性的几条限制:
如果一个特性不在当前作用域内,它就不能被实现。
不管是特性还是
impl,都只能在当前的包装箱内起作用。带有特性约束的泛型函数使用单态化实现 (monomorphization),
所以它是静态派分的 (statically dispatched)。
下面列举几个非常有用的标准库特性:
Drop提供了当一个值退出作用域后执行代码的功能,它只有一个drop(&mut self)方法。Borrow用于创建一个数据结构时把拥有和借用的值看作等同。AsRef用于在泛型中把一个值转换为引用。Deref<Target=T>用于把&U类型的值自动转换为&T类型。Iterator用于在集合 (collection) 和惰性值生成器 (lazy value generator) 上实现迭代器。Sized用于标记运行时长度固定的类型,而不定长的切片和特性必须放在指针后面使其运行时长度已知,比如
&[T]和Box<Trait>。
泛型和多态
泛型 (generics) 在类型理论中称作参数多态 (parametric polymorphism), 意为对于给定参数可以有多种形式的函数或类型。先看Rust中的一个泛型例子:
Option在rust标准库中的定义:
Option的典型用法:
其中<T>部分表明它是一个泛型数据类型。当然,泛型参数也可以用于函数参数和结构体域:
对于多态函数,存在两种派分 (dispatch) 机制:静态派分和动态派分。 前者类似于C++的模板,Rust会生成适用于指定类型的特殊函数,然后在被调用的位置进行替换, 好处是允许函数被内联调用,运行比较快,但是会导致代码膨胀 (code bloat); 后者类似于Java或Go的interface,Rust通过引入特性对象 (trait object) 来实现, 在运行期查找虚表 (vtable) 来选择执行的方法。特性对象&Foo具有和特性Foo相同的名称, 通过转换 (casting) 或者强制多态化 (coercing) 一个指向具体类型的指针来创建。
当然,特性也可以接受泛型参数。但是,往往更好的处理方式是使用关联类型 (associated type):
Last updated
Was this helpful?