关联类型在trait定义中指定占位符类型
关联类型(associated types)是一个将类型占位符与 trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型。 有trait的实现者指定具体的类型。 例如:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
之所使用关联类型而不是泛型: 当 trait 有泛型参数时,可以多次实现这个 trait,每次需改变泛型参数的具体类型。通过关联类型,则无需标注类型,因为不能多次实现这个 trait。
默认泛型类型参数和运算符重载
为泛型指定一个默认的具体类型,这样就不需要为默认类型编写实现trait的代码了。在声明泛型类型的时候使用<PlacehoulderType=ConcreteType>即可。ConcreteType不指定,那么默认就是Self类型。
Rust只能通过实现std::ops列出的运算符相应的trait来重载对应的运算符。
例如,通过实现Add trait来重载+运算符。
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
assert_eq!(
Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 }
);
}完全限定语义与消歧义
Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法,也不能阻止为同一类型同时实现这两个 trait。甚至直接在类型上实现开始已经有的同名方法也是可能的!
完全限定语法就是让用户调用指定的同名的方法。 例如:
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}直接调用Human实例的fly方法,会默认调用直接实现在类型上的方法
fn main() {
let person = Human;
person.fly(); //*waving arms furiously*
}为了能够调用 Pilot trait 或 Wizard trait 的 fly 方法,我们需要使用更明显的语法以便能指定我们指的是哪个 fly 方法。
fn main() {
let person = Human;
Pilot::fly(&person);
Wizard::fly(&person);
person.fly();
}但是关联函数没有self参数,没有办法使用上面的调用方式。就要使用完全限定语法。其语法为:
<Type as Trait>::function(receiver_if_method, next_arg, ...);例如如下的定义:
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
fn main() {
//A baby dog is called a Spot
println!("A baby dog is called a {}", Dog::baby_name());
}
默认也是直接定义在类型的函数。这里要使用Dog的Animal实现,需要:
fn main() {
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
supertrait:在另一个trait中使用某trait的功能
定义一个trait名为OutlinePrint,要使用Display trait的功能,那么其定义如下:
use std::fmt;
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}在这里就使用了Display trait里的to_string()方法。
一个类型要想实现OutlinePrint trait就必须实现Display trait。
newtype模式:在外部类型上实现外部trait
在trait中存在孤儿规则,trait或类型对于当前crate是本地的话就可以在此类型上是实现该trait。 使用newtype模式可以绕过孤儿规则。
利用元组结构体对类型进行封装,那么这个包装类型就是本地的了,就可以在这个类型上实现trait了。
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}如果希望新类型拥有其内部类型的每一个方法,为封装类型实现 Deref trait。不然就需要为包装类型实现其内部类型的每个方法。