长安的 Rust 入门教程长安的 Rust 入门教程
首页
基础教程
进阶内容
Rust 官网
编程指南
首页
基础教程
进阶内容
Rust 官网
编程指南
  • 进阶内容

    • 🚀 进阶内容
    • 第1章 - 泛型
    • 第2章 - Trait
    • 第3章 - 生命周期
    • 第4章 - 智能指针
    • 第5章 - 并发编程

第2章 - Trait

嗨,朋友!我是长安。

上一章我们学习了泛型,这一章要学习一个更强大的特性——Trait。

Trait 有点像其他语言中的"接口"(Interface),但功能更强大。它定义了一组方法,任何实现了这个 Trait 的类型都必须提供这些方法。

听起来有点抽象?别担心,我会用最简单的方式给你讲清楚。

🤔 什么是 Trait?

Trait 就是定义共同行为的方式。

想象一下,不同的动物都会"叫",但叫声不同:

  • 狗会"汪汪"
  • 猫会"喵喵"
  • 鸭子会"嘎嘎"

虽然叫声不同,但它们都有"叫"这个行为。在 Rust 中,我们可以用 Trait 来表示这种共同行为。

长安说

Trait 就像是一份合同或协议,规定了"如果你想成为某种类型,你必须能做这些事情"。

📖 定义 Trait

使用 trait 关键字定义:

trait Animal {
    fn make_sound(&self) -> String;
}

这个 Trait 定义了一个方法签名 make_sound,但没有实现。实现由具体的类型来完成。

🏗️ 实现 Trait

使用 impl Trait for Type 语法:

struct Dog {
    name: String,
}

struct Cat {
    name: String,
}

impl Animal for Dog {
    fn make_sound(&self) -> String {
        format!("{}说: 汪汪!", self.name)
    }
}

impl Animal for Cat {
    fn make_sound(&self) -> String {
        format!("{}说: 喵喵!", self.name)
    }
}

fn main() {
    let dog = Dog { name: String::from("旺财") };
    let cat = Cat { name: String::from("咪咪") };
    
    println!("{}", dog.make_sound());  // 旺财说: 汪汪!
    println!("{}", cat.make_sound());  // 咪咪说: 喵喵!
}

🎯 默认实现

Trait 可以提供默认实现:

trait Greet {
    fn greet(&self) -> String {
        String::from("你好!")  // 默认实现
    }
}

struct Person;
struct Robot;

impl Greet for Person {}  // 使用默认实现

impl Greet for Robot {
    fn greet(&self) -> String {  // 自定义实现
        String::from("beep boop, 你好!")
    }
}

fn main() {
    let person = Person;
    let robot = Robot;
    
    println!("{}", person.greet());  // 你好!
    println!("{}", robot.greet());   // beep boop, 你好!
}

🔗 Trait 作为参数

这是 Trait 最强大的用途之一:你可以接受任何实现了某个 Trait 的类型。

fn make_animal_sound(animal: &impl Animal) {
    println!("动物叫声: {}", animal.make_sound());
}

fn main() {
    let dog = Dog { name: String::from("旺财") };
    let cat = Cat { name: String::from("咪咪") };
    
    make_animal_sound(&dog);
    make_animal_sound(&cat);
}

&impl Animal 的意思是:"接受任何实现了 Animal Trait 的类型的引用"。

Trait Bound 语法

还有一种更正式的写法:

fn make_animal_sound<T: Animal>(animal: &T) {
    println!("动物叫声: {}", animal.make_sound());
}

<T: Animal> 叫做 Trait Bound(Trait 约束),表示泛型 T 必须实现 Animal Trait。

长安说

两种写法效果一样:

  • impl Trait 语法:简洁,适合简单情况
  • Trait Bound 语法:灵活,适合复杂情况

🎨 多个 Trait 约束

一个类型可以同时要求实现多个 Trait:

use std::fmt::Display;

fn print_and_sound<T: Animal + Display>(item: &T) {
    println!("信息: {}", item);  // 需要 Display
    println!("声音: {}", item.make_sound());  // 需要 Animal
}

使用 + 号连接多个 Trait。

where 子句

当约束很多时,可以用 where 子句让代码更清晰:

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

🔄 Trait 作为返回值

你也可以返回实现了某个 Trait 的类型:

fn get_animal(kind: &str) -> impl Animal {
    if kind == "dog" {
        Dog { name: String::from("旺财") }
    } else {
        Dog { name: String::from("默认狗") }
    }
}

注意

使用 impl Trait 作为返回值时,只能返回同一种具体类型。上面的例子都返回 Dog,所以没问题。

如果想返回不同类型,需要使用 Trait 对象(下面会讲)。

📦 常用的标准库 Trait

Rust 标准库提供了很多有用的 Trait:

1. Display 和 Debug

用于格式化输出:

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 p = Point { x: 3, y: 4 };
    println!("点: {}", p);  // 使用 Display
}

2. Clone 和 Copy

用于复制值:

#[derive(Clone, Copy)]  // 自动实现
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = p1;  // Copy
    let p3 = p1.clone();  // Clone
    
    println!("p1: ({}, {})", p1.x, p1.y);  // p1 依然有效
}

3. PartialEq 和 Eq

用于比较相等性:

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

fn main() {
    let p1 = Person { name: String::from("长安"), age: 25 };
    let p2 = Person { name: String::from("长安"), age: 25 };
    
    if p1 == p2 {
        println!("两个人相同");
    }
}

4. PartialOrd 和 Ord

用于比较大小:

#[derive(PartialEq, PartialOrd)]
struct Height(f64);

fn main() {
    let h1 = Height(1.75);
    let h2 = Height(1.80);
    
    if h1 < h2 {
        println!("h1 更矮");
    }
}

🎭 Derive 属性

对于常见的 Trait,Rust 可以自动生成实现:

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

fn main() {
    let user = User {
        name: String::from("长安"),
        age: 25,
    };
    
    println!("{:?}", user);  // Debug
    let user2 = user.clone();  // Clone
    println!("相等吗? {}", user == user2);  // PartialEq
}

常用的可 derive 的 Trait:

  • Debug - 调试输出
  • Clone - 克隆
  • Copy - 复制
  • PartialEq - 相等比较
  • Eq - 严格相等
  • PartialOrd - 大小比较
  • Ord - 严格排序
  • Hash - 哈希

🎯 实战例子:可排序的学生列表

#[derive(Debug, Clone, PartialEq, Eq)]
struct Student {
    name: String,
    score: u32,
}

impl PartialOrd for Student {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Student {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        // 按分数从高到低排序
        other.score.cmp(&self.score)
    }
}

fn main() {
    let mut students = vec![
        Student { name: String::from("小明"), score: 85 },
        Student { name: String::from("小红"), score: 92 },
        Student { name: String::from("小刚"), score: 78 },
    ];
    
    students.sort();  // 可以排序了!
    
    for student in students {
        println!("{}: {} 分", student.name, student.score);
    }
}

输出:

小红: 92 分
小明: 85 分
小刚: 78 分

🔍 Trait 对象

如果你想在运行时处理不同类型,可以使用 Trait 对象:

trait Animal {
    fn make_sound(&self) -> String;
}

struct Dog;
struct Cat;

impl Animal for Dog {
    fn make_sound(&self) -> String {
        String::from("汪汪!")
    }
}

impl Animal for Cat {
    fn make_sound(&self) -> String {
        String::from("喵喵!")
    }
}

fn main() {
    let animals: Vec<Box<dyn Animal>> = vec![
        Box::new(Dog),
        Box::new(Cat),
        Box::new(Dog),
    ];
    
    for animal in animals {
        println!("{}", animal.make_sound());
    }
}

dyn Animal 表示"实现了 Animal Trait 的某种类型"。

长安说

  • impl Trait:编译时确定类型(静态分发,快)
  • dyn Trait:运行时确定类型(动态分发,灵活但稍慢)

💡 小结

在这一章,我们学会了:

  • Trait 定义共同行为
  • 使用 trait 定义,impl Trait for Type 实现
  • 默认实现简化代码
  • Trait 作为参数接受多种类型
  • Trait Bound 约束泛型
  • Derive 属性自动实现常用 Trait
  • Trait 对象实现运行时多态

核心要点:

  • Trait 像接口,定义类型必须实现的方法
  • 可以有默认实现
  • impl Trait vs Trait Bound:&impl T vs <T: Trait>
  • derive 可以自动实现常用 Trait
  • dyn Trait 用于动态分发

长安的建议

Trait 是 Rust 最强大的特性之一,配合泛型使用威力无穷!刚开始可能觉得复杂,但多写几次就会发现它让代码变得更灵活、更安全。

记住:Trait 就是"这个类型能做什么"的约定!

🚀 下一步

现在你已经掌握了泛型和 Trait,下一章我们要学习一个更高级的概念——生命周期。

生命周期帮助 Rust 确保引用总是有效的,这是 Rust 安全性的另一个重要支柱!

第3章 - 生命周期 →

💪 练习题

  1. 定义一个 Describable Trait,有一个 describe() 方法返回字符串,为 Book 和 Movie 实现它
  2. 写一个函数,接受任何实现了 Display 和 Clone Trait 的类型,打印并克隆它
  3. 为自定义类型实现 PartialEq,让两个实例可以用 == 比较
  4. 创建一个 Trait 对象的 Vec,存储不同类型但都实现了同一个 Trait
答案示例
use std::fmt::Display;

// 练习1
trait Describable {
    fn describe(&self) -> String;
}

struct Book {
    title: String,
    author: String,
}

struct Movie {
    title: String,
    director: String,
}

impl Describable for Book {
    fn describe(&self) -> String {
        format!("《{}》 by {}", self.title, self.author)
    }
}

impl Describable for Movie {
    fn describe(&self) -> String {
        format!("《{}》 directed by {}", self.title, self.director)
    }
}

// 练习2
fn print_and_clone<T: Display + Clone>(item: &T) -> T {
    println!("打印: {}", item);
    item.clone()
}

// 练习3
#[derive(Clone)]
struct Product {
    name: String,
    price: f64,
}

impl PartialEq for Product {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name && self.price == other.price
    }
}

// 练习4
trait Shape {
    fn area(&self) -> f64;
}

struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 {
        3.14159 * self.radius * self.radius
    }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn main() {
    let shapes: Vec<Box<dyn Shape>> = vec![
        Box::new(Circle { radius: 5.0 }),
        Box::new(Rectangle { width: 4.0, height: 6.0 }),
    ];
    
    for shape in shapes {
        println!("面积: {}", shape.area());
    }
}
最近更新: 2025/12/26 18:01
Contributors: 王长安
Prev
第1章 - 泛型
Next
第3章 - 生命周期