rust语言

【RUST_BASIC】Rust 高级 trait

2021-11-28  本文已影响0人  ixiaolong

1 关联类型

关联类型(associated types)是一个将类型占位符与 trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型:

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
}

trait 的实现者会针对特定的实现在这个类型的位置指定相应的具体类型:

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        // --snip--

只能选择一次 Item 的类型,因为只能有一个 impl Iterator for Counter。当调用 Counternext 时不必每次指定我们需要 u32 值的迭代器。

2 trait 中的默认泛型

当使用泛型类型参数时,可以为泛型指定一个默认的具体类型。如果默认类型就足够的话,这消除了为具体类型实现 trait 的需要。为泛型类型指定默认类型的语法是在声明泛型类型时使用 ·<PlaceholderType=ConcreteType>·:

trait Add<RHS=Self> {
    type Output;

    fn add(self, rhs: RHS) -> Self::Output;
}

<RHS=Self> 语法叫做默认类型参数(default type parameters),RHS 是一个泛型类型参数(“right hand side” 的缩写),它用于定义 add 方法中的 rhs 参数,如果实现 Add trait 时不指定 RHS 的具体类型,RHS 的类型将是默认的 Self 类型。

不带参数如下:

use std::ops::Add;

#[derive(Debug, 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 });
}

带参数如下:

use std::ops::Add;

struct Millimeters(u32);
struct Meters(u32);

impl Add<Meters> for Millimeters {
    type Output = Millimeters;

    fn add(self, other: Meters) -> Millimeters {
        Millimeters(self.0 + (other.0 * 1000))
    }
}

默认参数类型主要用于如下两个方面:

3 完全限定语法

一个 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*");
    }
}

可如下进行方法的区分:

fn main() {
    let person = Human;
    Pilot::fly(&person);
    Wizard::fly(&person);
    person.fly();
}

但是关联函数是 trait 的一部分,但没有 self 参数,当同一作用域的两个类型实现了同一 trait,Rust 就不能计算出我们期望的是哪一个类型:

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());
}

当调用 Animal::baby_name() 将报错,Animal::baby_name 是关联函数而不是方法,因此它没有 self 参数,无法计算出所需的是哪一个 Animal::baby_name 实现,此时需要使用完全限定语法(fully qualified syntax):

fn main() {
    println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}

通常,完全限定语法定义为:

<Type as Trait>::function(receiver_if_method, next_arg, ...);

只有当存在多个同名实现而 Rust 需要帮助以便知道调用哪个实现时,才需要使用这个较为冗长的语法。

4 newtype 模式用以在外部类型上实现外部 trait

如果想要在 Vec<T> 上实现 Display,而孤儿规则阻止,因为 Display trait 和 Vec<T> 都定义于我们的 crate 之外。

一个绕开这个限制的方法是使用 newtype 模式:可以创建一个包含 Vec<T> 实例的 Wrapper 结构体,在 Wrapper 上实现 Display 并使用 Vec<T> 的值:

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);
}

此方法的缺点是,因为 Wrapper 是一个新类型,它没有定义于其值之上的方法,必须直接在 Wrapper 上实现 Vec<T> 的所有方法,这样就可以代理到 self.0 上,如果希望新类型拥有其内部类型的每一个方法,可为封装类型实现 Deref trait,并返回其内部类型是一种解决方案;如果不希望封装类型拥有所有内部类型的方法,则必须只自行实现所需的方法。

上一篇下一篇

猜你喜欢

热点阅读