用户工具

站点工具


rust:第十三章trait

第十三章 Trait

13.1 什么是Trait

Trait定义了类型可以共享的行为。类似于其他语言中的接口(interface),但功能更强大。

Trait的作用:

  • 定义共享的行为
  • 实现泛型编程
  • 提供抽象接口

13.2 定义和实现Trait

定义Trait

pub trait Summary {
    fn summarize(&self) -> String;
}

实现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

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条新推文:{}", tweet.summarize());

13.3 默认实现

Trait可以提供默认实现:

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}
 
impl Summary for NewsArticle {}  // 使用默认实现
 
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

默认实现可以调用其他方法:

pub trait Summary {
    fn summarize_author(&self) -> String;
 
    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}
 
impl Summary for Tweet {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}

13.4 Trait作为参数

impl Trait语法

pub fn notify(item: impl Summary) {
    println!("突发新闻!{}", item.summarize());
}

Trait Bound语法

pub fn notify<T: Summary>(item: T) {
    println!("突发新闻!{}", item.summarize());
}

多个参数:

// impl Trait
pub fn notify(item1: impl Summary, item2: impl Summary) {
    // item1和item2可以是不同类型
}
 
// Trait Bound(强制相同类型)
pub fn notify<T: Summary>(item1: T, item2: T) {
    // item1和item2必须是相同类型
}

13.5 多个Trait Bound

使用+语法

use std::fmt::Display;
 
pub fn notify(item: impl Summary + Display) {
    println!("突发新闻!{}", item.summarize());
}
 
// 或使用where子句
pub fn notify<T>(item: T)
where
    T: Summary + Display,
{
    println!("突发新闻!{}", item.summarize());
}

13.6 返回实现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,
    }
}

限制: 只能返回单一具体类型

// 错误!不能返回不同类型的impl Trait
fn returns_summarizable(switch: bool) -> impl Summary {
    if switch {
        NewsArticle {
            headline: String::from("..."),
            location: String::from("..."),
            author: String::from("..."),
            content: String::from("..."),
        }
    } else {
        Tweet {
            username: String::from("..."),
            content: String::from("..."),
            reply: false,
            retweet: false,
        }
    }
}

13.7 使用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 }
    }
}
 
impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("最大的成员是 x = {}", self.x);
        } else {
            println!("最大的成员是 y = {}", self.y);
        }
    }
}

13.8 使用Trait Bound有条件地实现Trait

use std::fmt::Display;
 
struct Wrapper<T>(Vec<T>);
 
impl<T: Display> Display for Wrapper<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[")?;
 
        for (i, v) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{}", v)?;
        }
 
        write!(f, "]")
    }
}

为任何实现了Display的类型实现ToString:

impl<T: Display> ToString for T {
    // ...
}

这称为 blanket implementations(全覆盖实现)。

13.9 常用标准库Trait

Debug

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

PartialEq和Eq

#[derive(PartialEq, Eq)]
struct Point {
    x: i32,
    y: i32,
}

PartialOrd和Ord

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Point {
    x: i32,
    y: i32,
}

Clone和Copy

#[derive(Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

Default

#[derive(Default)]
struct Config {
    debug: bool,
    timeout: u32,
}
 
let config = Config::default();

13.10 完整示例:媒体播放器

// 定义Playable trait
pub trait Playable {
    fn play(&self);
    fn pause(&self) {
        println!("Paused");
    }
    fn stop(&self);
}
 
// 定义音频
pub struct Audio {
    title: String,
    artist: String,
}
 
impl Audio {
    pub fn new(title: &str, artist: &str) -> Self {
        Audio {
            title: title.to_string(),
            artist: artist.to_string(),
        }
    }
}
 
impl Playable for Audio {
    fn play(&self) {
        println!("Playing audio: {} by {}", self.title, self.artist);
    }
 
    fn stop(&self) {
        println!("Stopped audio: {}", self.title);
    }
}
 
// 定义视频
pub struct Video {
    title: String,
    resolution: (u32, u32),
}
 
impl Video {
    pub fn new(title: &str, width: u32, height: u32) -> Self {
        Video {
            title: title.to_string(),
            resolution: (width, height),
        }
    }
}
 
impl Playable for Video {
    fn play(&self) {
        println!("Playing video: {} at {}x{}", 
                 self.title, self.resolution.0, self.resolution.1);
    }
 
    fn pause(&self) {
        println!("Video paused at {}x{}", 
                 self.resolution.0, self.resolution.1);
    }
 
    fn stop(&self) {
        println!("Stopped video: {}", self.title);
    }
}
 
// 播放列表
pub struct Playlist {
    items: Vec<Box<dyn Playable>>,
}
 
impl Playlist {
    pub fn new() -> Self {
        Playlist { items: Vec::new() }
    }
 
    pub fn add(&mut self, item: Box<dyn Playable>) {
        self.items.push(item);
    }
 
    pub fn play_all(&self) {
        for item in &self.items {
            item.play();
        }
    }
}
 
fn main() {
    let mut playlist = Playlist::new();
 
    playlist.add(Box::new(Audio::new("Song", "Artist")));
    playlist.add(Box::new(Video::new("Movie", 1920, 1080)));
 
    playlist.play_all();
}

练习题

练习题13.1:实现Comparable trait

trait Comparable {
    fn compare(&self, other: &Self) -> i32;
}
 
struct Rectangle {
    width: u32,
    height: u32,
}
 
impl Comparable for Rectangle {
    fn compare(&self, other: &Self) -> i32 {
        let area1 = self.width * self.height;
        let area2 = other.width * other.height;
 
        if area1 > area2 {
            1
        } else if area1 < area2 {
            -1
        } else {
            0
        }
    }
}
 
fn main() {
    let r1 = Rectangle { width: 10, height: 20 };
    let r2 = Rectangle { width: 5, height: 30 };
 
    match r1.compare(&r2) {
        1 => println!("r1更大"),
        -1 => println!("r2更大"),
        _ => println!("一样大"),
    }
}

练习题13.2:实现Printable trait

trait Printable {
    fn print(&self);
}
 
impl Printable for i32 {
    fn print(&self) {
        println!("整数:{}", self);
    }
}
 
impl Printable for String {
    fn print(&self) {
        println!("字符串:{}", self);
    }
}
 
impl<T: Printable> Printable for Vec<T> {
    fn print(&self) {
        println!("向量:[");
        for item in self {
            print!("  ");
            item.print();
        }
        println!("]");
    }
}
 
fn main() {
    let numbers = vec![1, 2, 3];
    numbers.print();
}

本章小结

本章学习了Rust的Trait系统:

  • 定义Trait:trait关键字定义共享行为
  • 实现Trait:impl Trait for Type
  • 默认实现:提供默认方法体
  • Trait作为参数:impl Trait或T: Trait
  • Trait Bound:约束泛型类型
  • 条件实现:基于Trait Bound实现方法和Trait

Trait是Rust实现多态和代码复用的核心机制,理解Trait对于掌握Rust至关重要。

rust/第十三章trait.txt · 最后更改: 127.0.0.1