🎃个人专栏:
🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客
🐳Java基础:Java基础_IT闫的博客-CSDN博客
🐋c语言:c语言_IT闫的博客-CSDN博客
🐟MySQL:数据结构_IT闫的博客-CSDN博客
🐠数据结构:数据结构_IT闫的博客-CSDN博客
💎C++:C++_IT闫的博客-CSDN博客
🥽C51单片机:C51单片机(STC89C516)_IT闫的博客-CSDN博客
💻基于HTML5的网页设计及应用:基于HTML5的网页设计及应用_IT闫的博客-CSDN博客
🥏python:python_IT闫的博客-CSDN博客
🐠离散数学:离散数学_IT闫的博客-CSDN博客
🥽Linux:Linux_Y小夜的博客-CSDN博客
🚝Rust:Rust_Y小夜的博客-CSDN博客
欢迎收看,希望对大家有用!
目录
🎯Trait简介
🎯定义一个Trait
🎯在类型上实现trait
🎯实现trait的约束
🎯默认实现
🎯trait作为参数
🎯Trait Bound语法
🎯通过+指定多个trait bound
🎯通过where简化trait bound
🎯实现Trait作为返回类型
🎯使用Trait Bound有条件的实现方法
🎯Trait简介
trait 定义了某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共同行为。可以使用 trait bounds 指定泛型是任何拥有特定行为的类型。
注意:trait 类似于其他语言中的常被称为 接口(interfaces)的功能,虽然有一些不同。
- Trait :抽象的定义共享行为。
- Trait bounds(约束):泛型类型参数指定为实现特定行为的参数。
- Trait与其他语言的接口(interface)类似,但有些区别。
🎯定义一个Trait
一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合。
展示了一个表现这个概念的公有
Summary
trait 的定义:pub trait Summary {fn summarize(&self) -> String; }
在方法签名后跟分号,而不是在大括号中提供其实现。接着每一个实现这个 trait 的类型都需要提供其自定义行为的方法体,编译器也会确保任何实现
Summary
trait 的类型都拥有与这个签名的定义完全一致的summarize
方法。trait 体中可以有多个方法:一行一个方法签名且都以分号结尾。
只有只有方法签名,没有具体实现。
实现该trait的类型必须提供具体的方法实现。
🎯在类型上实现trait
pub struct NewsArticle {pub headline: String,pub location: String,pub author: String,pub content: String, }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)} }
在类型上实现 trait 类似于实现与 trait 无关的方法。区别在于
impl
关键字之后,我们提供需要实现 trait 的名称,接着是for
和需要实现 trait 的类型的名称。在impl
块中,使用 trait 定义中的方法签名,不过不再后跟分号,而是需要在大括号中编写函数体来为特定类型实现 trait 方法所拥有的行为。
🎯实现trait的约束
现在库在
NewsArticle
和Tweet
上实现了Summary
trait,crate 的用户可以像调用常规方法一样调用NewsArticle
和Tweet
实例的 trait 方法了。唯一的区别是 trait 必须和类型一起引入作用域以便使用额外的 trait 方法。这是一个二进制 crate 如何利用aggregator
库 crate 的例子:use aggregator::{Summary, Tweet};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()); }
不能为外部类型实现外部 trait。例如,不能在
aggregator
crate 中为Vec<T>
实现Display
trait。这是因为Display
和Vec<T>
都定义于标准库中,它们并不位于aggregator
crate 本地作用域中。这个限制是被称为 相干性(coherence)的程序属性的一部分,或者更具体的说是 孤儿规则(orphan rule),其得名于不存在父类型。这条规则确保了其他人编写的代码不会破坏你代码,反之亦然。没有这条规则的话,两个 crate 可以分别对相同类型实现相同的 trait,而 Rust 将无从得知应该使用哪一个实现。
🎯默认实现
有时为 trait 中的某些或全部方法提供默认的行为,而不是在每个类型的每个实现中都定义自己的行为是很有用的。这样当为某个特定类型实现 trait 时,可以选择保留或重载每个方法的默认行为。
pub trait Summary {fn summarize(&self) -> String {String::from("(Read more...)")} }
如果想要对
NewsArticle
实例使用这个默认实现,可以通过impl Summary for NewsArticle {}
指定一个空的impl
块。默认实现允许调用相同 trait 中的其他方法,哪怕这些方法没有默认实现。
pub trait Summary {fn summarize_author(&self) -> String;fn summarize(&self) -> String {format!("(Read more from {}...)", self.summarize_author())} }
为了使用这个版本的
Summary
,只需在实现 trait 时定义summarize_author
即可:impl Summary for Tweet {fn summarize_author(&self) -> String {format!("@{}", self.username)} }
注意无法从相同方法的重载实现中调用默认方法。
🎯trait作为参数
pub fn notify(item: &impl Summary) {println!("Breaking news! {}", item.summarize()); }
对于
item
参数,我们指定了impl
关键字和 trait 名称,而不是具体的类型。该参数支持任何实现了指定 trait 的类型。适用于简单的情况。
🎯Trait Bound语法
impl Trait
语法适用于直观的例子,它实际上是一种较长形式我们称为 trait bound语法的语法糖。它看起来像:pub fn notify<T: Summary>(item: &T) {println!("Breaking news! {}", item.summarize()); }
这与之前的例子相同,不过稍微冗长了一些。trait bound 与泛型参数声明在一起,位于尖括号中的冒号后面。
impl Trait
很方便,适用于短小的例子。更长的 trait bound 则适用于更复杂的场景。pub fn notify(item1: &impl Summary, item2: &impl Summary) {
这适用于
item1
和item2
允许是不同类型的情况(只要它们都实现了Summary
)。不过如果你希望强制它们都是相同类型呢?这只有在使用 trait bound 时才有可能:pub fn notify<T: Summary>(item1: &T, item2: &T) {
🎯通过+指定多个trait bound
如果
notify
需要显示item
的格式化形式,同时也要使用summarize
方法,那么item
就需要同时实现两个不同的 trait:Display
和Summary
。pub fn notify(item: &(impl Summary + Display)) {
+
语法也适用于泛型的 trait bound:pub fn notify<T: Summary + Display>(item: &T) {
🎯通过where简化trait bound
使用过多的 trait bound 也有缺点。每个泛型有其自己的 trait bound,所以有多个泛型参数的函数在名称和参数列表之间会有很长的 trait bound 信息,这使得函数签名难以阅读。为此,Rust 有另一个在函数签名之后的
where
从句中指定 trait bound 的语法。所以除了这么写:fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
还可以像这样使用
where
从句:fn some_function<T, U>(t: &T, u: &U) -> i32 whereT: Display + Clone,U: Clone + Debug, {
🎯实现Trait作为返回类型
fn returns_summarizable() -> impl Summary {Tweet {username: String::from("horse_ebooks"),content: String::from("of course, as you probably already know, people",),reply: false,retweet: false,} }
返回一个只是指定了需要实现的 trait 的类型的能力在闭包和迭代器场景十分的有用不过。
这只适用于返回单一类型的情况。
🎯使用Trait Bound有条件的实现方法
通过使用带有 trait bound 的泛型参数的
impl
块,可以有条件地只为那些实现了特定 trait 的类型实现方法。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 }} }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);}} }
也可以对任何实现了特定 trait 的类型有条件地实现 trait。对任何满足特定 trait bound 的类型实现 trait 被称为 blanket implementations,它们被广泛的用于 Rust 标准库中。