34_trait

1. 概述

Trait告诉Rust编译器,某种类型具有哪些并且可以与其他类型共享的功能,可以使用抽象的方式定义共享行为。Trait bounds(约束)指的是泛型类型参数指定实现了特定行为的类型。

Trait与其他语言的借口(interface)类似,但是有区别。

2. 定义Trait

定义Trait的目的是把方法签名放在一起,来实现某种目的所必须的一组行为。定义trait的相关规则如下

  • 关键字:trait
  • 只有方法签名,没有具体实现
  • trait可以有多个方法,每个方法占一行,以;结尾
  • 实现该trait的类型必须提供具体方法的实现

如下定义trait的示例代码

Rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}

3. 在类型上实现trait

与类型的实现方法类似,如下示例代码

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

fn main() {
    let tweet = Tweet {
        username: String::from("horse_ebooks"),
        content: String::from("of course, as you probably already know, people"),
        reply: false,
        retweet: false
    };

    println!("1 new tweet: {}", tweet.summarize());
}

4. 实现某个trait的约束

可以在某个类型上实现某个trait的前提条件为:这个类型或这个trait在本地crate里定义的。

无法为外部类型来实现外部的trait,这个限制是程序属性的一部分(也就是一致性),更具体地说是孤儿原则,之所以这样命名是因为父类型不存在。此规则确保他人的代码不能破坏你写的代码,反之亦然。如果没有这个规则,两个crate可以为同一个类型实现同一个trait,rust就不知道应该使用哪个实现了。

5. 默认实现

trait可以拥有默认实现,当我们为某个类型实现trait时,可以选择重载或者保留原有的默认实现。默认实现的方法也可以调用trait中其他的方法,即使这些方法没有默认实现。如下示例代码

Rust 复制代码
pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

pub struct NewArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewArticle {
    fn summarize_author(&self) -> String {
        format!("@{} ", self.author)
    }
}

需要注意的是,我们无法在方法的重写实现里调用默认的实现。

6. 使用trait作为参数

6.1 impl Trait

使用impl Trait的语法,可以适用于简单的函数传参情况。加入我们希望传入的参数实现了一个Summary这个trait,那么我们可以使用如下的参数声明方式

Rust 复制代码
pub fn notify(item: impl Summary) {
    println("breaking news! {}", item.summarize());
}

那么我们在调用此函数的使用,可以传入Tweet,也可以传入NewArticle

6.2 Trait bound

使用Trait bound可以与复杂的情况,如下示例代码

Rust 复制代码
pub fn notify<T: Summary>(item: T) {
    println("breaking news! {}", item.summarize());
}

如果宝航两个参数,使用Trait bound的写法如下

Rust 复制代码
pub fn notify<T: Summary>(itema: T, item2: T) {
    println("breaking news! {}", item1.summarize());
}

实际上impl TraitTrait bound的语法糖。

6.3 使用+指定多个trait bound

如果使用impl Trait的方式,示例代码如下

Rust 复制代码
use std::fmt::Display;

pub fn notify(item: impl Summary + Display) {
    println("breaking news! {}", item.summarize());
}

使用Trait bound的写法如下

Rust 复制代码
use std::fmt::Display;

pub fn notify<T: Summary + Display>(item: T) {
    println("breaking news! {}", item.summarize());
}

6.4 在方法签名后面使用where子句

如果一个函数要求的变量需要实现过多的Trait,代码的可读性会变低,很不直观,如下示例代码

Rust 复制代码
pub fn notify<T: Summary + Display, U: Clone + Debug>(a: T, b: U) -> String {
    format!("breaking news! {}", a.summarize());
}

我们可以使用where子句还简化Trait的约束,如下示例代码

Rust 复制代码
pub fn notify<T, U>(a: T, b: U) -> String
where T: Summary + Display,
    U: Clone + Debug,
{
    format!("breaking news! {}", a.summarize());
}

7. 使用trait作为返回值类型

使用impl Trait声明返回的类型为Trait,如下示例代码

Rust 复制代码
pub fn notify(flag: bool) -> impl Summary {
    // ......
}

使用imple Trait只能返回确定的同一种类型,返回可能不同类型的代码会报错,如下代码将报错

Rust 复制代码
pub fn notify(flag: bool) -> impl Summary {
    if flag {
        NewArticle {
            // ......
        }
    } else {
        Tweet {
            // ......
        }
    }
}

8. 使用PartialOrd实现数据比较

如下示例代码

rust 复制代码
use std::result;

fn largest<T: PartialOrd +Clone>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list.iter() {
        if item > &largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let str_list = vec![String::from("hello"), String::from("world")];
    let result = largest(&str_list);
    println!("The largest word id {}", result);
}

9. 使用Trait Bound有条件的实现方法

在使用泛型类型参数的 impl 块上使用Trait bound,我们可以有条件的胃了实现特定 trait 的类型来实现。如下示例代码

Rust 复制代码
use std::fmt::Display;

struct Pair<T> {
    x: T,
    y: T,
}

// 所有Pair<T> 都拥有new函数
impl <T> Pair<T>{
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

// 实现了Display 和 PartialOrd 的Pair<T> 猜拥有com_display函数
impl <T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("the largest member is x = {}", self.x);
        } else {
            println!("the largest member is y = {}", self.y);
        }
    }
}

fn main(){

}

也可以为实现其他Trait的类型有条件的实现某个Trait。为满足Trait Bound的所有类型上实现Trait叫做覆盖实现(Blanket implementations)。

相关推荐
不能只会打代码14 分钟前
六十天前端强化训练之第一天HTML5语义化标签深度解析与博客搭建实战
前端·html·html5
OpenTiny社区31 分钟前
Node.js技术原理分析系列——Node.js的perf_hooks模块作用和用法
前端·node.js
菲力蒲LY35 分钟前
输入搜索、分组展示选项、下拉选取,全局跳转页,el-select 实现 —— 后端数据处理代码,抛砖引玉展思路
java·前端·mybatis
MickeyCV2 小时前
Nginx学习笔记:常用命令&端口占用报错解决&Nginx核心配置文件解读
前端·nginx
祈澈菇凉2 小时前
webpack和grunt以及gulp有什么不同?
前端·webpack·gulp
zy0101012 小时前
HTML列表,表格和表单
前端·html
初辰ge2 小时前
【p-camera-h5】 一款开箱即用的H5相机插件,支持拍照、录像、动态水印与样式高度定制化。
前端·相机
HugeYLH3 小时前
解决npm问题:错误的代理设置
前端·npm·node.js
六个点3 小时前
DNS与获取页面白屏时间
前端·面试·dns
道不尽世间的沧桑3 小时前
第9篇:插槽(Slots)的使用
前端·javascript·vue.js