概述

特质(Trait)是 Rust 中定义共享行为的方式。类似于其他语言中的接口,但更强大。特质允许你定义抽象行为,并为不同类型提供实现。

定义特质

基本 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

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

Trait Bound

Trait Bound 语法

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

Trait Bound 语法(完整形式)

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

多个 Trait Bound

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

where 子句

fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{
    // 函数体
}

返回实现了 Trait 的类型

返回 impl 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,
    }
}

限制

// 错误:不能返回不同类型
fn returns_summarizable(switch: bool) -> impl Summary {
    if switch {
        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."),
        }
    } else {
        Tweet {
            username: String::from("horse_ebooks"),
            content: String::from("of course, as you probably already know, people"),
            reply: false,
            retweet: false,
        }
    }
}

默认实现

定义默认实现

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(阅读更多...)")
    }
}

使用默认实现

impl Summary for NewsArticle {}

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."),
    };
    
    println!("新文章可用! {}", article.summarize());
}

调用默认实现

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(阅读更多...)")
    }
    
    fn summarize_author(&self) -> String {
        format!("@{}", self.summarize())
    }
}

Trait 作为参数

使用 impl Trait

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

使用 Trait Bound

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

多个 Trait Bound

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

where 子句

fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{
    // 函数体
}

Trait Bound 与生命周期

Trait Bound 中的生命周期

use std::fmt::Display;

fn longest_with_an_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

标准库 Trait

Display Trait

use std::fmt;

struct Point {
    x: i32,
    y: i32,
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

fn main() {
    let point = Point { x: 3, y: 4 };
    println!("点: {}", point);
}

Debug Trait

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

fn main() {
    let point = Point { x: 3, y: 4 };
    println!("点: {:?}", point);
}

Clone Trait

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

fn main() {
    let p1 = Point { x: 3, y: 4 };
    let p2 = p1.clone();
    println!("p1: ({}, {}), p2: ({}, {})", p1.x, p1.y, p2.x, p2.y);
}

Copy Trait

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

fn main() {
    let p1 = Point { x: 3, y: 4 };
    let p2 = p1;  // p1 仍然有效
    println!("p1: ({}, {}), p2: ({}, {})", p1.x, p1.y, p2.x, p2.y);
}

PartialEq 和 Eq Trait

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

fn main() {
    let p1 = Point { x: 3, y: 4 };
    let p2 = Point { x: 3, y: 4 };
    println!("p1 == p2: {}", p1 == p2);
}

PartialOrd 和 Ord Trait

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

fn main() {
    let p1 = Point { x: 3, y: 4 };
    let p2 = Point { x: 5, y: 6 };
    println!("p1 < p2: {}", p1 < p2);
}

Hash Trait

use std::collections::HashMap;
use std::hash::Hash;

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

fn main() {
    let mut map = HashMap::new();
    let p = Point { x: 3, y: 4 };
    map.insert(p, "原点");
    println!("map: {:?}", map);
}

Trait 对象

Trait 对象的基本概念

pub trait Draw {
    fn draw(&self);
}

pub struct Button {
    pub width: u32,
    pub height: u32,
    pub label: String,
}

impl Draw for Button {
    fn draw(&self) {
        println!("绘制按钮: {} ({}x{})", self.label, self.width, self.height);
    }
}

pub struct TextField {
    pub width: u32,
    pub height: u32,
    pub placeholder: String,
}

impl Draw for TextField {
    fn draw(&self) {
        println!("绘制文本框: {} ({}x{})", self.placeholder, self.width, self.height);
    }
}

使用 Trait 对象

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>,
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

fn main() {
    let screen = Screen {
        components: vec![
            Box::new(Button {
                width: 50,
                height: 10,
                label: String::from("OK"),
            }),
            Box::new(TextField {
                width: 75,
                height: 10,
                placeholder: String::from("输入文本"),
            }),
        ],
    };
    
    screen.run();
}

Trait 对象的限制

// 错误:Trait 对象必须是对象安全的
pub trait Clone {
    fn clone(&self) -> Self;
}

// 正确:使用泛型
pub fn clone<T: Clone>(item: &T) -> T {
    item.clone()
}

关联类型

定义关联类型

pub trait Iterator {
    type Item;
    
    fn next(&mut self) -> Option<Self::Item>;
}

实现关联类型

struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = u32;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

使用关联类型

fn main() {
    let mut counter = Counter::new();
    
    while let Some(value) = counter.next() {
        println!("值: {}", value);
    }
}

实际应用示例

创建一个可比较的 Trait

trait Comparable {
    fn compare(&self, other: &Self) -> Ordering;
}

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

impl Comparable for Person {
    fn compare(&self, other: &Self) -> Ordering {
        self.age.cmp(&other.age)
    }
}

fn main() {
    let p1 = Person {
        name: String::from("Alice"),
        age: 30,
    };
    
    let p2 = Person {
        name: String::from("Bob"),
        age: 25,
    };
    
    match p1.compare(&p2) {
        Ordering::Less => println!("{} 比 {} 年轻", p1.name, p2.name),
        Ordering::Equal => println!("{} 和 {} 同龄", p1.name, p2.name),
        Ordering::Greater => println!("{} 比 {} 年长", p1.name, p2.name),
    }
}

创建一个可序列化的 Trait

trait Serializable {
    fn serialize(&self) -> String;
    fn deserialize(s: &str) -> Result<Self, String>
    where
        Self: Sized;
}

struct Point {
    x: i32,
    y: i32,
}

impl Serializable for Point {
    fn serialize(&self) -> String {
        format!("{},{}", self.x, self.y)
    }
    
    fn deserialize(s: &str) -> Result<Self, String> {
        let parts: Vec<&str> = s.split(',').collect();
        if parts.len() != 2 {
            return Err("无效的格式".to_string());
        }
        
        let x = parts[0].parse::<i32>()
            .map_err(|_| "无效的 x 坐标".to_string())?;
        let y = parts[1].parse::<i32>()
            .map_err(|_| "无效的 y 坐标".to_string())?;
        
        Ok(Point { x, y })
    }
}

fn main() {
    let p = Point { x: 3, y: 4 };
    let serialized = p.serialize();
    println!("序列化: {}", serialized);
    
    let deserialized = Point::deserialize(&serialized).unwrap();
    println!("反序列化: ({}, {})", deserialized.x, deserialized.y);
}

总结

本教程详细介绍了 Rust 的特质(Trait):

  1. 定义和实现 Trait

    • 使用 trait 关键字定义
    • 使用 impl 为类型实现 Trait
    • Trait Bound 语法
  2. 默认实现

    • 为 Trait 方法提供默认实现
    • 可以调用默认实现
  3. Trait 作为参数

    • impl Trait 语法
    • Trait Bound 语法
    • where 子句
  4. 标准库 Trait

    • Display、Debug
    • Clone、Copy
    • PartialEq、Eq
    • PartialOrd、Ord
    • Hash
  5. Trait 对象

    • 使用 dyn 关键字
    • 动态分发
    • 对象安全限制
  6. 关联类型

    • 在 Trait 中定义类型占位符
    • 在实现中指定具体类型

Trait 是 Rust 中实现多态和代码复用的核心机制,掌握 Trait 对于编写灵活、可扩展的 Rust 代码至关重要。

下一步

在下一教程中,我们将学习 Rust 的并发编程,包括:

  • 线程

  • 消息传递

  • 共享状态

  • Sync 和 Send Trait

  • 异步编程基础

继续学习 Rust,掌握这门强大语言的更多特性!