美文网首页rust语言
【RUST_BASIC】Rust 高级 trait

【RUST_BASIC】Rust 高级 trait

作者: ixiaolong | 来源:发表于2021-11-28 00:40 被阅读0次

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,并返回其内部类型是一种解决方案;如果不希望封装类型拥有所有内部类型的方法,则必须只自行实现所需的方法。

相关文章

  • 【RUST_BASIC】Rust 高级 trait

    1 关联类型 关联类型(associated types)是一个将类型占位符与 trait 相关联的方式,这样 t...

  • rust Iterator

    Rust Iterator设计: 定义: 对Iterator Trait的理解: Rust的Iterator在大部...

  • 【RUST_BASIC】Rust 并发

    1 线程 调用 thread::spawn 函数创建线程并传递一个闭包,包含新线程运行的代码: 使用 join 等...

  • 【RUST_BASIC】Rust 宏

    参考 https://kaisery.github.io/trpl-zh-cn/ch19-06-macros.ht...

  • [Rust]Trait

    trait定义了某一个类型所具有的特定行为,跟Java中的抽象类有类似,但有一些区别。trait中可以包含常量,函...

  • Rust Trait

    观感 Rust的Trait和Golang的interface看起来非常相似,从开发者角度来看,都可以实现具体类型的...

  • Rust for cpp dev - Trait 的高级用法

    在之前的章节中,我们的代码中出现了一些 trait 的用法,但是并没有展开来讲。本章我们将集中介绍 trait 的...

  • Rust学习───trait

    什么是trait?如果了解Java语言的话,你就可以把trait理解为Java的interface接口 定义pub...

  • Rust impl trait

    trait特性 trait特性可以理解为Java中的接口,具备和接口很类似的特性。trait中的函数叫做方法。某个...

  • rust-trait

    什么是trait,trait相对于java就是interface。 基本的trait例子 在trait中,由sel...

网友评论

    本文标题:【RUST_BASIC】Rust 高级 trait

    本文链接:https://www.haomeiwen.com/subject/weofxrtx.html