Rust进阶[part5]_trait

Rust进阶[part5]_trait
SoniaChenRust进阶[part5]_trait
trait概述
在 Rust 中,trait
是一种定义共享行为的方式。它类似于其他语言中的接口,允许我们定义一组方法签名,然后让不同的类型去实现这些方法。通过 trait
,我们可以实现多态性,即不同类型可以以统一的方式处理。
普通实现
- 使用
trait
关键字来声明一个特征 summary
是特征名- 在大括号中定义了该特征的所有方法
// 定义一个 trait
trait Summary {
fn summarize(&self) -> String;
}
// 定义一个结构体
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
// 为 NewsArticle 结构体实现 Summary trait
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
// 定义另一个结构体
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
// 为 Tweet 结构体实现 Summary trait
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Penguins win the Stanley Cup Championship!"),
location: String::from("Pittsburgh, PA, USA"),
author: String::from("Iceburgh"),
content: String::from("The Pittsburgh Penguins once again are the best \
hockey team in the NHL."),
};
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
reply: false,
retweet: false,
};
println!("Article summary: {}", article.summarize());
println!("Tweet summary: {}", tweet.summarize());
}
特征定义与实现的位置
孤儿规则
孤儿规则是 Rust 中的一个重要规则,它规定了特征的实现必须满足以下条件:要么特征在当前的 crate 中定义,要么类型在当前的 crate 中定义。这个规则确保了特征的实现是可预测的,避免了不同 crate 中对同一类型实现同一特征时可能出现的冲突。
例如,我们不能在自己的 crate 中为 Vec<T>
实现标准库中的 Display
特征,因为 Vec<T>
和 Display
都定义在标准库中。
带泛型的 trait
可以针对不同的泛型来实现
默认泛型类型参数
- 实现 trait 时不指定
Rhs
的具体类型,Rhs
的类型将是默认的Self
类型,也就是在其上实现Add
的类型
trait Add<Rhs=Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
// 为 i32 实现 Add trait
impl Add for i32 {
type Output = i32;
fn add(self, rhs: i32) -> i32 {
self + rhs
}
}
fn main() {
let num1 = 5;
let num2 = 3;
let result = num1.add(num2);
println!("The result of addition is: {}", result);
}
关联类型
关联类型是 trait 定义中的类型占位符,并不定义它的具体类型是什么,在实现这个 trait 的时候才为这个关联类型赋予确定的类型。
trait Container {
type Item;
fn contains(&self, item: &Self::Item) -> bool;
}
struct IntContainer {
numbers: Vec<i32>,
}
impl Container for IntContainer {
type Item = i32;
fn contains(&self, item: &i32) -> bool {
self.numbers.contains(item)
}
}
fn main() {
let container = IntContainer { numbers: vec![1, 2, 3] };
println!("Container contains 2: {}", container.contains(&2));
}
特征作为函数参数
&impl
使用 &impl Trait
语法可以将实现了特定 trait 的类型作为参数传递给函数。
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("Headline: {}", self.headline)
}
}
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
fn main() {
let article = NewsArticle {
headline: String::from("New Rust feature released!"),
};
notify(&article);
}
trait bound语法
trait bound
语法允许我们在泛型函数中指定泛型类型必须实现的 trait。
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
where 语法简化 trait bound
当泛型参数较多,并且每个参数都有多个 trait 约束时,使用 where
语法可以使代码更易读。
fn notify<T>(item: &T)
where
T: Summary + Display,
{
println!("Breaking news! {}", item.summarize());
item.display();
}
trait作为泛型的类型
可以将 trait 作为泛型类型参数,这样函数就可以接受实现了该 trait 的任何类型。
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("Headline: {}", self.headline)
}
}
fn print_summary<T: Summary>(item: T) {
println!("Summary: {}", item.summarize());
}
fn main() {
let article = NewsArticle {
headline: String::from("New Rust feature released!"),
};
print_summary(article);
}
impl trait 语法 (返回值中返回trait)
如果需要动态返回不同的 trait 类型的话,需要使用 Box<dyn xxx>
语法来保证返回的内存大小一致,并且可以标注返回的哪种 trait。
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("Headline: {}", self.headline)
}
}
fn returns_summarizable() -> Box<dyn Summary> {
Box::new(NewsArticle {
headline: String::from("New Rust feature released!"),
})
}
fn main() {
let item = returns_summarizable();
println!("Summary: {}", item.summarize());
}
通过 derive
派生类型
例如 #[derive(Debug)]
是一种特征派生语法,这种被标记的对象会自动实现对应的默认特征代码,继承相应的功能。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect = Rectangle { width: 30, height: 50 };
println!("Rectangle: {:?}", rect);
}
练习
练习 1
定义一个 Area
trait,包含一个 area
方法,用于计算面积。然后为 Rectangle
结构体实现该 trait。
练习 2
创建一个函数,接受一个实现了 Area
trait 的类型作为参数,并打印其面积。
练习 3
使用 derive
派生 Clone
和 Copy
trait 到一个自定义类型上,并验证其功能。
以下是练习的参考答案:
// 练习 1
trait Area {
fn area(&self) -> f64;
}
struct Rectangle {
width: f64,
height: f64,
}
impl Area for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
// 练习 2
fn print_area<T: Area>(shape: T) {
println!("The area is: {}", shape.area());
}
// 练习 3
#[derive(Clone, Copy, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let rect = Rectangle { width: 5.0, height: 3.0 };
print_area(rect);
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 复制操作
println!("p1: {:?}, p2: {:?}", p1, p2);
}