🦀 第10课:trait与trait bound

trait是Rust的抽象机制,类似其他语言的接口(interface),但更强大。它定义了类型可以实现的行为,配合泛型的trait bound,让你写出既灵活又安全的代码。

核心特性 第10/25课

学习目标:定义和实现trait、默认方法、trait bound、trait对象、运算符重载

📋 定义和实现trait

use std::fmt;

// 定义trait
trait Summary {
    fn summarize_author(&self) -> String;
    
    // 默认实现
    fn summarize(&self) -> String {
        format!("(阅读更多来自{}的内容...)", self.summarize_author())
    }
}

struct Article {
    title: String,
    author: String,
    content: String,
}

struct Tweet {
    username: String,
    content: String,
}

// 为类型实现trait
impl Summary for Article {
    fn summarize_author(&self) -> String {
        self.author.clone()
    }
    
    // 覆盖默认实现
    fn summarize(&self) -> String {
        format!("{},作者{}", self.title, self.author)
    }
}

impl Summary for Tweet {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
    // 使用默认的summarize
}

fn main() {
    let article = Article {
        title: "Rust 1.95发布".to_string(),
        author: "Rust团队".to_string(),
        content: "新版本带来了...".to_string(),
    };
    println!("{}", article.summarize());
    
    let tweet = Tweet {
        username: "rustlang".to_string(),
        content: "Rust 1.95 is out!".to_string(),
    };
    println!("{}", tweet.summarize());
}
Rust 1.95发布,作者Rust团队 (阅读更多来自@rustlang的内容...)

✅ 验证通过

🔗 trait作为参数

trait Draw {
    fn draw(&self);
}

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

impl Draw for Circle {
    fn draw(&self) {
        println!("⭕ 圆 (半径={})", self.radius);
    }
}

impl Draw for Rectangle {
    fn draw(&self) {
        println!("▬ 矩形 ({}×{})", self.width, self.height);
    }
}

impl Draw for Triangle {
    fn draw(&self) {
        println!("△ 三角形 (底={}, 高={})", self.base, self.height);
    }
}

// 方式1: impl Trait语法(静态分发)
fn draw_shape(shape: &impl Draw) {
    shape.draw();
}

// 等价写法(泛型 + trait bound)
fn draw_shape_generic(shape: &T) {
    shape.draw();
}

// 多个trait bound
fn draw_and_debug(shape: &(impl Draw + std::fmt::Debug)) {
    println!("调试: {:?}", shape);
    shape.draw();
}

// 方式2: trait对象(动态分发)
fn draw_all(shapes: &[Box]) {
    for shape in shapes {
        shape.draw();
    }
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rect = Rectangle { width: 3.0, height: 4.0 };
    
    draw_shape(&circle);
    draw_shape(&rect);
    
    // 异构集合 —— 只有trait对象能做到
    let shapes: Vec> = vec![
        Box::new(Circle { radius: 2.0 }),
        Box::new(Rectangle { width: 3.0, height: 4.0 }),
        Box::new(Triangle { base: 5.0, height: 3.0 }),
    ];
    
    println!("\n绘制所有图形:");
    draw_all(&shapes);
}
⭕ 圆 (半径=5) ▬ 矩形 (3×4) 绘制所有图形: ⭕ 圆 (半径=2) ▬ 矩形 (3×4) △ 三角形 (底=5, 高=3)

✅ 验证通过

🎯 trait bound详解

use std::fmt::Display;

// where子句 —— 更清晰的trait bound写法
fn longest_with_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: Display,
{
    println!("📢 公告: {}", ann);
    if x.len() > y.len() { x } else { y }
}

// 条件实现
struct Pair {
    x: T,
    y: T,
}

impl Pair {
    fn new(x: T, y: T) -> Self {
        Pair { x, y }
    }
}

// 只有T实现了Display + PartialOrd才能cmp_display
impl Pair {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("x >= y: {} >= {}", self.x, self.y);
        } else {
            println!("x < y: {} < {}", self.x, self.y);
        }
    }
}

// blanket implementation(覆盖实现)
// 标准库示例:impl ToString for T { ... }
// 任何实现了Display的类型都自动实现了ToString

fn main() {
    let result = longest_with_announcement("hello", "hi there", "比较字符串");
    println!("更长的: {}", result);
    
    let pair = Pair::new(3.14, 2.71);
    pair.cmp_display();
    
    // ToString就是通过blanket impl从Display自动获得的
    let s = 42.to_string();  // i32实现了Display → 自动有ToString
    println!("数字转字符串: {}", s);
}
📢 公告: 比较字符串 更长的: hi there x >= y: 3.14 >= 2.71 数字转字符串: 42

✅ 验证通过

⚡ 运算符重载

use std::ops::{Add, Mul, Neg};

#[derive(Debug, Clone, Copy, PartialEq)]
struct Vec2 {
    x: f64,
    y: f64,
}

impl Vec2 {
    fn new(x: f64, y: f64) -> Self {
        Vec2 { x, y }
    }
    
    fn dot(&self, other: &Vec2) -> f64 {
        self.x * other.x + self.y * other.y
    }
    
    fn magnitude(&self) -> f64 {
        self.dot(self).sqrt()
    }
    
    fn normalize(&self) -> Self {
        let mag = self.magnitude();
        Vec2 { x: self.x / mag, y: self.y / mag }
    }
}

impl Add for Vec2 {
    type Output = Vec2;
    fn add(self, rhs: Vec2) -> Vec2 {
        Vec2 { x: self.x + rhs.x, y: self.y + rhs.y }
    }
}

impl Mul for Vec2 {
    type Output = Vec2;
    fn mul(self, scalar: f64) -> Vec2 {
        Vec2 { x: self.x * scalar, y: self.y * scalar }
    }
}

impl Neg for Vec2 {
    type Output = Vec2;
    fn neg(self) -> Vec2 {
        Vec2 { x: -self.x, y: -self.y }
    }
}

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

fn main() {
    let a = Vec2::new(3.0, 4.0);
    let b = Vec2::new(1.0, 2.0);
    
    println!("a = {}", a);
    println!("b = {}", b);
    println!("a + b = {}", a + b);
    println!("a * 2 = {}", a * 2.0);
    println!("-a = {}", -a);
    println!("a·b = {}", a.dot(&b));
    println!("|a| = {:.2}", a.magnitude());
    println!("a归一化 = {}", a.normalize());
}
a = (3, 4) b = (1, 2) a + b = (4, 6) a * 2 = (6, 8) -a = (-3, -4) a·b = 11 |a| = 5.00 a归一化 = (0.6, 0.8)

✅ 验证通过

🏗️ 综合实战:插件系统

use std::fmt;

// 插件trait
trait Plugin: fmt::Debug {
    fn name(&self) -> &str;
    fn version(&self) -> &str;
    fn initialize(&mut self) -> Result<(), String>;
    fn execute(&self, input: &str) -> String;
    fn shutdown(&mut self) {}
}

// 日志插件
#[derive(Debug)]
struct LoggerPlugin {
    initialized: bool,
    log_count: usize,
}

impl LoggerPlugin {
    fn new() -> Self {
        LoggerPlugin { initialized: false, log_count: 0 }
    }
}

impl Plugin for LoggerPlugin {
    fn name(&self) -> &str { "Logger" }
    fn version(&self) -> &str { "1.0.0" }
    fn initialize(&mut self) -> Result<(), String> {
        self.initialized = true;
        Ok(())
    }
    fn execute(&self, input: &str) -> String {
        format!("[LOG] {}", input)
    }
    fn shutdown(&mut self) {
        println!("Logger插件关闭,共记录{}条日志", self.log_count);
    }
}

// 转换插件
#[derive(Debug)]
struct UppercasePlugin {
    initialized: bool,
}

impl UppercasePlugin {
    fn new() -> Self {
        UppercasePlugin { initialized: false }
    }
}

impl Plugin for UppercasePlugin {
    fn name(&self) -> &str { "Uppercase" }
    fn version(&self) -> &str { "2.0.0" }
    fn initialize(&mut self) -> Result<(), String> {
        self.initialized = true;
        Ok(())
    }
    fn execute(&self, input: &str) -> String {
        input.to_uppercase()
    }
}

// 插件管理器
struct PluginManager {
    plugins: Vec<Box,
}

impl PluginManager {
    fn new() -> Self {
        PluginManager { plugins: Vec::new() }
    }
    
    fn register(&mut self, mut plugin: Box) -> Result<(), String> {
        plugin.initialize()?;
        println!("✅ 注册插件: {} v{}", plugin.name(), plugin.version());
        self.plugins.push(plugin);
        Ok(())
    }
    
    fn execute_all(&self, input: &str) -> Vec<String> {
        self.plugins.iter()
            .map(|p| {
                let result = p.execute(input);
                format!("[{}] {}", p.name(), result)
            })
            .collect()
    }
    
    fn list(&self) {
        println!("已注册插件 ({}个):", self.plugins.len());
        for p in &self.plugins {
            println!("  - {} v{}", p.name(), p.version());
        }
    }
}

fn main() {
    let mut manager = PluginManager::new();
    
    manager.register(Box::new(LoggerPlugin::new())).unwrap();
    manager.register(Box::new(UppercasePlugin::new())).unwrap();
    
    manager.list();
    
    let results = manager.execute_all("Hello, Rust!");
    for result in results {
        println!("{}", result);
    }
}
✅ 注册插件: Logger v1.0.0 ✅ 注册插件: Uppercase v2.0.0 已注册插件 (2个): - Logger v1.0.0 - Uppercase v2.0.0 [Logger] [LOG] Hello, Rust! [Uppercase] HELLO, RUST!

✅ 验证通过

📝 练习

练习1:Describable trait

定义Describable trait,包含describe(&self) -> String方法。为i32、String、Vec<T>实现它。

练习2:Sortable trait

定义Sortable trait,包含sort_key(&self) -> i64。实现一个通用排序函数sort_by_key<T: Sortable>(items: &mut [T])

练习3:运算符重载

Complex<f64>实现Add、Sub、Mul和Display trait。

🏆 本课成就

🔒 下一课解锁:生命周期 —— 引用有效性的保证

🔧 关联类型与trait对象

use std::fmt;

// 关联类型
trait Container {
    type Item;
    fn get(&self, index: usize) -> Option<&Self::Item>;
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool { self.len() == 0 }
}

struct Array3 { data: [T; 3] }

impl Container for Array3 {
    type Item = T;
    fn get(&self, index: usize) -> Option<&T> { self.data.get(index) }
    fn len(&self) -> usize { 3 }
}

// trait对象与动态分发
trait Drawable {
    fn draw(&self);
    fn area(&self) -> f64;
}

struct Circle { radius: f64 }
struct Square { side: f64 }

impl Drawable for Circle {
    fn draw(&self) { println!("⭕ 圆 r={}", self.radius); }
    fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }
}

impl Drawable for Square {
    fn draw(&self) { println!("⬛ 方 s={}", self.side); }
    fn area(&self) -> f64 { self.side * self.side }
}

fn total_area(shapes: &[Box]) -> f64 {
    shapes.iter().map(|s| s.area()).sum()
}

fn main() {
    let arr = Array3 { data: [10, 20, 30] };
    for i in 0..arr.len() {
        println!("arr[{}] = {}", i, arr.get(i).unwrap());
    }
    
    let shapes: Vec> = vec![
        Box::new(Circle { radius: 5.0 }),
        Box::new(Square { side: 4.0 }),
    ];
    for s in &shapes { s.draw(); }
    println!("总面积: {:.2}", total_area(&shapes));
}
arr[0] = 10 arr[1] = 20 arr[2] = 30 ⭕ 圆 r=5 ⬛ 方 s=4 总面积: 91.54

✅ 验证通过