Trait
在 Rust 官方教程里,Trait 用于“定义共同行为”。简单来说,不同的数据类型可能都需要某种功能,这样的共同功能就可以定义成 Trait,提供更统一的调用方式。(就算你不感兴趣也建议读一下,能让你更了解 Rust 底层)
以下内容中的部分程序引用了 Rust 官方教程中的程序。
定义 Trait
Rust 官方文档里举的例子是“Summary”(概括)函数(可是我觉得这个功能和“概括”没多大关系)。
// pub 表示这是一个“公共”的内容,可以被其他源文件调用(导出)
// trait 用于定义 Trait
// Summary 是名称
pub trait Summary {
fn summarize(&self) -> String;
// Trait 里可以有多个函数,你可以不写实现,只定义名称
}
加上默认实现
pub trait Summary {
// 这里的函数体,在你没有为一种数据类型定义实现时,会是 Trait 的实现方法
// 函数一般写上 &self 这个参数,用于调用被操作的数据
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
调用同 Trait 中的其他函数
pub trait Summary {
fn summarize_author(&self) -> String;
// 这里 summarize 函数调用了 summarize_author 函数,那么在大部分情况下,我们就只用实现 summarize_author 函数 了
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
为数据类型实现 Trait
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
// impl Trait的名称 for 数据类型 {}
impl Summary for NewsArticle {
// 这里写你想为该数据类型自定义实现的函数
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)
}
}
如果你用了上面第三个版本的 Summary Trait,你可以这样写:
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
// 可以只定义自己需要的函数
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
Trait 作为参数
还记得泛型吗?如果你想做一个用于所有有 Summary Trait 的数据类型的函数,你可以怎么写?
将 Trait 作为参数是一种解决方法。
// 公共函数 notify 有一个参数 item,类型是 Trait Summary 的引用
// 其实你可以将参数中的 Trait 看作 所有实现了该 Trait 的数据类型的集合。
// 这样 item 就可以是上面 NewsArticle 和 Tweet 中的一种。
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
Trait Bound 语法:为泛型加条件
// <T: Summary> 是一个泛型的声明
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
在有多个 Trait 作为参数时,使用第一种方法和 Trait Bound 得到的结果是不同的。
在 pub fn notify(item1: &impl Summary, item2: &impl Summary) {
中,item1 和 item2 只要都实现了 Summary Trait 就行了,不一定是同一数据类型。
在 pub fn notify<T: Summary>(item1: &T, item2: &T) {
中,泛型 T 只能指一种数据类型,所以 item1 和 item2 一定是同一种数据类型。
多个 Trait 作为参数
使用普通语法:
pub fn notify(item: &(impl Summary + Display)) {
使用 Trait Bound:
pub fn notify<T: Summary + Display>(item: &T) {
这里,item 的数据类型会同时满足 有 Summary Trait 和 有 Display Trait。
在返回值中使用 Trait 类型
// 返回值类型是 impl Trait的名称
fn returns_summarizable() -> impl Summary {
Trait Bound 的另一种写法
// <T, U>: 泛型的定义
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone, // T 满足的条件
U: Clone + Debug, // U 满足的条件
{
运用 Trait Bound ,有条件地实现方法
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 这里 T 代表的数据类型要满足有 Display 和 PartialOrd 两个 Trait
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 <T: ParitalOrd + PartialEq> larger_item(a: T, b: T) -> T {
if a >= b {
return a;
}
else {
return b;
}
}
简单说一下常见的 Trait:
PartialOrd
提供 > >= < <=
PartialEq
提供 == !=
+ - * /
等可以看“运算符重载”中“算数运算符的重载”中的“可重载的算数运算符表”。在这里:panjk0518
此后会有一段时间不会更新,因为我要去学写图形化程序。