关联类型在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。不然就需要为包装类型实现其内部类型的每个方法。