1. 在rust中运用相关类型来制定占位类型

相关类型(assciated type)是trait中类型占位符,它能够用于trait的办法签名中。详细地说能够定义出包含某些类型的trait,而在完成前无需知道这些类型是什么。

如下示例代码:

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}
fn main() {
    println!("Hello, world");
}

2. 相关类型与泛型的差异

每次完成泛型trait的时分必须标示详细的类型,而且能够为一个类型屡次完成某个Trait(运用不同的泛型参数);而完成相关类型trait的时分,咱们无需标示类型,但是要在里边指明详细的相关类型,而且咱们无法为单个类型屡次完成某个Trait。

如下示例代码:

// 相关类型Trait
pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}
// 泛型类型Trait
pub trait Iterator2<T> {
    fn next(&mut self) -> Option<T>;
}
// struct
struct Counter {}
// 完成相关类型的Trait
impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}
// 完成泛型类型的trait
impl Iterator2<String> for Counter {
    fn next(&mut self) -> Option<String> {
        None
    }
}
// 再次完成泛型类型的trait
impl Iterator2<u32> for Counter {
    fn next(&mut self) -> Option<u32> {
        None
    }
}
fn main() {
    println!("Hello, world!");
}

3. 默许泛型参数和运算符重载

能够在运用泛型参数时为泛型指定一个默许的详细类型,语法为:<PlaceholderType=ConcreteType>,这种技术常用于运算符重载(operator overloading)。尽管Rust不允许创立自己的运算符以及重载任意的运算符,但能够经过完成std::ops中列出的那些trait来重载一部分相应的运算符。

如下示例代码:

use std::ops::Add;
#[derive(Debug, PartiaEq)]
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 });
}

在上面的示例代码中,std::ops::Add trait 运用默许的泛型参数类型为 <Rhs = Self>,也便是说,在咱们完成std::ops::Add trait的过程中,如果没有为Rhs指定一个详细的参数类型,那么Rhs类型就默许为Self,所以上面的代码中Rhs便是SelfPoint

下面咱们做一个毫米和米相加的比如,就需求指定泛型参数类型。

如下示例代码:

use std::ops::Add;
struct Millimeters(u32);
struct Meters(u32);
impl Add(Meters) for Millimeters {
    type Output = Millimeters;
    fn add(self, other: Point) -> Point {
        Millimeters(self.0 + (other.o * 1000))
    }
}
fn main() {
}

4. 默许泛型参数的主要运用场景

主要有两个运用场景,如下

  • 扩展一个类型而不损坏现有的代码
  • 允许在大部分用户都不需求的特定场景下进行自定义

5. 彻底限制语法(Fully Qualified Syntax)与怎么调用同名办法

如下示例代码:

trait Pilot {
    fn fly(&self);
}
trait Wizard {
    fn fly(&self);
}
struct Human;
impl Pilot for Human {
    fn fly(&self) {
        println!("This is your caption speaking.")
    }
}
impl Wizard for Human {
    fn fly(&self) {
        println!("Up!");
    }
}
impl Human {
    fn fly(&self) {
        println!("*waving arms furiously*");
    }
}
fn mian() {
    let person = Human;
    person.fly();
}

在上面示例代码中,两个struct别离完成了Humantrait,完成了fly办法,而且Human trait有自己的fly办法。最终在main函数中调用了fly办法,那么会调用到哪个办法呢?在上面的示例代码中,将调用自身的fly办法。此刻又有一个问题,那么怎么调用完成了两个trait的办法。

如下示例代码:

fn mian() {
    let person = Human;
    // 调用自身的办法
    person.fly();
    // 调用Pilot的办法
    Pilot::fly(&person);
    // 调用Wizard的办法
    Wizard::fly(&person);
}

需求留意的是,Pilot能够被多个struct完成,那么怎么知道为什么调的是Human的办法,是因为有self参数,传入的&person参数中它便是Human类型。咱们再来看一个不一样的示例

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() {
    println!("A baby dog is called a {}", Dog::baby_name());
}

在上面的示例代码中,Dog自身有baby_name相关办法,它输出的是“Spot”,那么怎么调用Animal的办法呢?这时分咱们能够运用彻底限制语法。

彻底限制语法的格式为:<Type as Trait>::function(receiver_if_method, next_arg, ...),这种语法能够在任何调用函数或办法的地方运用,而且它允许疏忽那些从上下文能推导出来的部分。但要记住的是,当rust无法区别咱们调用哪个详细完成的时分,才需运用这种语法,因为这种语法写起来比较麻烦,不应轻易运用。如下示例代码

// ...
fn main() {
    // 调用自身的办法
    println!("A baby dog is called a {}", Dog::baby_name());
    // 调用完成Animal的办法
    println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}

6. 运用supertrait来要求trait附带其他trait的功用

其实相当于一个trait承继另外一个trait。有时分咱们需求再一个trait中运用其他trait的功用,也便是说需求直接被依靠的trait也被完成,那个被直接依靠的trait便是当前trait的supertrait。假如咱们希望一个trait有打印功用的办法,并希望打印的办法能够调用to_string办法,完成如下示例代码:

use std::fmt;
// 依靠于fmt::Display
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));
    }
}
struct Point {
    x: i32,
    y: i32,
}
// 为Point完成OutlinePrint
impl OutlinePrint for Point {}
// Point完成了OutlinePrint就必须完成fmt::Display
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}
fn main() {}

7. 运用newtype形式在外部类型上完成外部trait

孤儿规矩:只有当trait或类型定义在本地包时,才能为该类型完成这个trait。咱们能够运用newtype形式来绕过这规矩,即运用tuple struct(元组结构体)创立一个新的类型放在本地。如咱们想为Vec完成fmt::Display trait,而Vecfmt::Display都在定义在外部包中,所以咱们无法直接为Vec完成fmt::Display

完成方案如下:

use std::fmt;
// 定义Wrapper元组结构体,包住Vec
struct Wrapper(Vec<String>);
// 为Wrapper完成Display
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // 运用selef.0即取出vec
        write!("[{}]", selef.0.join(", "));
    }
}
fn main() {
    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("w = {}", w);
}