【Rust入门知识点学与练】第21课:Trait 进阶 Advanced Traits

知识点:关联类型

关联类型让 trait 的使用者不需要指定泛型参数:

rust 复制代码
// 用泛型:每次实现都要指定具体类型
trait Container<T> {
    fn get(&self) -> &T;
}

// 用关联类型:trait 内部声明类型,实现时指定
trait Iterator {
    type Item;  // 关联类型

    fn next(&mut self) -> Option<Self::Item>;
}

struct Counter {
    count: usize,
    max: usize,
}

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

// 实现时指定 Item 为 usize
impl Iterator for Counter {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < self.max {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

fn main() {
    let mut counter = Counter::new(5);
    while let Some(val) = counter.next() {
        print!("{} ", val);
    }
    println!();  // 1 2 3 4 5
}

知识点:默认泛型参数和运算符重载

rust 复制代码
use std::ops::Add;

// Add trait 的定义(标准库中)
// trait Add<Rhs = Self> {     // Rhs 有默认值 Self
//     type Output;
//     fn add(self, rhs: Rhs) -> Self::Output;
// }

// 默认 Rhs = Self,所以同类型相加不需要指定
#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

// 不同类型相加:指定 Rhs
#[derive(Debug, Clone, Copy)]
struct Mm(f64);

#[derive(Debug, Clone, Copy)]
struct Cm(f64);

// Cm + Mm => Cm
impl Add<Mm> for Cm {
    type Output = Cm;

    fn add(self, rhs: Mm) -> Cm {
        Cm(self.0 + rhs.0 / 10.0)
    }
}

fn main() {
    let p1 = Point { x: 1.0, y: 2.0 };
    let p2 = Point { x: 3.0, y: 4.0 };
    println!("{:?}", p1 + p2);  // Point { x: 4.0, y: 6.0 }

    let length = Cm(3.0);
    let extra = Mm(50.0);
    println!("{:?}", length + extra);  // Cm(8.0)
}

知识点:完全限定语法 --- 消除歧义

当一个类型实现了多个有同名方法的 trait 时,需要完全限定:

rust 复制代码
trait Pilot {
    fn fly(&self);
}

trait Wizard {
    fn fly(&self);
}

struct Human;

impl Pilot for Human {
    fn fly(&self) {
        println!("飞行员在飞");
    }
}

impl Wizard for Human {
    fn fly(&self) {
        println!("巫师在飞");
    }
}

impl Human {
    fn fly(&self) {
        println!("人类在扑腾翅膀");
    }
}

fn main() {
    let h = Human;

    // 默认调用 Human 自身的方法
    h.fly();  // 人类在扑腾翅膀

    // 完全限定语法调用 trait 方法
    Pilot::fly(&h);   // 飞行员在飞
    Wizard::fly(&h);  // 巫师在飞

    // 关联函数(没有 self 参数)的完全限定
    trait Animal {
        fn name() -> String;
    }

    struct Dog;

    impl Dog {
        fn name() -> String {
            String::from("小狗")
        }
    }

    impl Animal for Dog {
        fn name() -> String {
            String::from("犬类")
        }
    }

    println!("{}", Dog::name());          // 小狗(调用自身方法)
    println!("{}", <Dog as Animal>::name());  // 犬类(调用 trait 方法)
}

知识点:超 trait(Supertrait)

一个 trait 可以要求实现者同时实现另一个 trait:

rust 复制代码
use std::fmt;

// OutlinePrint 要求实现者必须也实现 fmt::Display
trait OutlinePrint: fmt::Display {
    fn outline(&self) {
        let text = self.to_string();  // 因为实现了 Display,所以可以用 to_string()
        let len = text.len();
        println!("{}", "*".repeat(len + 4));
        println!("* {} *", text);
        println!("{}", "*".repeat(len + 4));
    }
}

struct Message(String);

// 必须先实现 Display,才能实现 OutlinePrint
impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl OutlinePrint for Message {}

// 另一个例子:要求能比较 + 能显示
trait Printable: fmt::Display + PartialOrd {
    fn print_comparison(&self, other: &Self) {
        if self < other {
            println!("{} < {}", self, other);
        } else if self > other {
            println!("{} > {}", other, self);
        } else {
            println!("{} == {}", self, other);
        }
    }
}

#[derive(PartialEq, PartialOrd)]
struct Score(i32);

impl fmt::Display for Score {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "分数({})", self.0)
    }
}

impl Printable for Score {}

fn main() {
    let msg = Message(String::from("Hello"));
    msg.outline();
    // *********
    // * Hello *
    // *********

    let s1 = Score(80);
    let s2 = Score(95);
    s1.print_comparison(&s2);  // 分数(80) < 分数(95)
}

知识点:newtype 模式

用元组结构体包装外部类型,为它实现本地 trait:

rust 复制代码
use std::fmt;

// Wrapper 包装了 Vec<String>
struct Wrapper(Vec<String>);

// 标准库的 Display trait 和 Vec<String> 都在外部 crate 中
// 不能直接为 Vec<String> 实现 Display(孤儿规则)
// 但可以为 Wrapper 实现!
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

// 也可以实现其他方法
impl Wrapper {
    fn push(&mut self, item: String) {
        self.0.push(item);
    }

    fn get(&self, index: usize) -> Option<&String> {
        self.0.get(index)
    }

    fn len(&self) -> usize {
        self.0.len()
    }
}

fn main() {
    let mut w = Wrapper(vec![
        String::from("hello"),
        String::from("world"),
    ]);

    println!("{}", w);  // [hello, world]

    w.push(String::from("rust"));
    println!("{}", w);  // [hello, world, rust]
    println!("长度: {}", w.len());  // 3
    println!("第一个: {:?}", w.get(0));  // Some("hello")
}

核心规则

概念 写法

关联类型 trait T { type Item; }

实现关联类型 impl T for X { type Item = Y; }

默认泛型参数 trait Add<Rhs = Self>

运算符重载 impl Add for Type { ... }

完全限定(方法) Trait::method(&instance)

完全限定(关联函数) ::function()

超 trait trait A: B { ... }

newtype 模式 struct Wrapper(ExternalType);

动手试试

补全下面的代码:

rust 复制代码
use std::fmt;
use std::ops::{Add, Mul};

// 补全:定义一个 trait Matrix,包含:
// 1. 关联类型 Element(要求实现 Add + Mul + Copy + Default + fmt::Display)
// 2. 方法 rows(&self) -> usize
// 3. 方法 cols(&self) -> usize
// 4. 方法 get(&self, row: usize, col: usize) -> Self::Element
// 5. 方法 set(&mut self, row: usize, col: usize, value: Self::Element)

// 补全:定义结构体 SimpleMatrix<E: Copy + Default>,内部用 Vec<Vec<E>> 存储

// 补全:为 SimpleMatrix<E> 实现 Matrix trait
// 其中 Element = E

// 补全:为 SimpleMatrix<E> 实现 Display trait
// 格式:每行用空格分隔,行间用换行
// 例如 2x3 矩阵:
// 1 2 3
// 4 5 6

// 补全:实现函数 transpose
// 接受一个实现了 Matrix 的引用,返回转置后的 SimpleMatrix
// 转置:行列互换
fn transpose<M: Matrix>(matrix: &M) -> SimpleMatrix<M::Element>
where
    M::Element: Copy + Default,
{
    // 补全
    todo!()
}

fn main() {
    // 创建一个 2x3 矩阵
    let data = vec![
        vec![1, 2, 3],
        vec![4, 5, 6],
    ];
    let matrix = SimpleMatrix { data };

    // 补全:打印矩阵
    // 期望输出:
    // 1 2 3
    // 4 5 6

    // 补全:打印行数和列数
    // 期望: 行数: 2, 列数: 3

    // 补全:转置并打印
    // 期望输出:
    // 1 4
    // 2 5
    // 3 6
}

期望输出:

1 2 3

4 5 6

行数: 2, 列数: 3

1 4

2 5

3 6

写好了贴过来,没问题就进入第22课:宏 Macros 👋

贴代码

rust 复制代码
  
use std::fmt;
use std::ops::{Add, Mul};

// Matrix trait,带关联类型 Element
trait Matrix {
    type Element: Add + Mul + Copy + Default + fmt::Display;

    fn rows(&self) -> usize;
    fn cols(&self) -> usize;
    fn get(&self, row: usize, col: usize) -> Self::Element;
    fn set(&mut self, row: usize, col: usize, value: Self::Element);
}

// SimpleMatrix 结构体
#[derive(Debug)]
struct SimpleMatrix<E: Copy + Default> {
    data: Vec<Vec<E>>,
}

// 为 SimpleMatrix 实现 Matrix trait
impl<E> Matrix for SimpleMatrix<E>
where
    E: Add + Mul + Copy + Default + fmt::Display,
{
    type Element = E;

    fn rows(&self) -> usize {
        self.data.len()
    }

    fn cols(&self) -> usize {
        if self.data.is_empty() {
            0
        } else {
            self.data[0].len()
        }
    }

    fn get(&self, row: usize, col: usize) -> Self::Element {
        self.data[row][col]
    }

    fn set(&mut self, row: usize, col: usize, value: Self::Element) {
        self.data[row][col] = value;
    }
}

// Display 实现
impl<E> fmt::Display for SimpleMatrix<E>
where
    E: Copy + Default + fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for row in &self.data {
            let line: Vec<String> = row.iter().map(|x| format!("{}", x)).collect();
            writeln!(f, "{}", line.join(" "))?;
        }
        Ok(())
    }
}

// 矩阵转置
fn transpose<M: Matrix>(matrix: &M) -> SimpleMatrix<M::Element>
where
    M::Element: Copy + Default,
{
    let r = matrix.rows();
    let c = matrix.cols();
    let mut transposed_data = vec![vec![M::Element::default(); r]; c];

    for i in 0..r {
        for j in 0..c {
            transposed_data[j][i] = matrix.get(i, j);
        }
    }

    SimpleMatrix {
        data: transposed_data,
    }
}

fn main() {
    // 创建一个 2x3 矩阵
    let data = vec![vec![1, 2, 3], vec![4, 5, 6]];
    let matrix = SimpleMatrix { data };

    // 打印矩阵
    println!("{}", matrix);

    // 打印行数和列数
    println!("行数: {}, 列数: {}", matrix.rows(), matrix.cols());

    // 转置并打印
    let t = transpose(&matrix);
    println!("{}", t);
}

运行输出

plaintext

1 2 3

4 5 6

行数: 2, 列数: 3

1 4

2 5

3 6

知识点解析

1. Trait + 关联类型(associated type)

rust 复制代码
  
trait Matrix {
    type Element: Add + Mul + Copy + Default + fmt::Display;
    ...
}
  • 关联类型:trait 内部定义一个类型占位符,实现的时候指定具体类型;
  • 和泛型 trait Matrix 对比:关联类型意味着一个实现只能对应一种元素类型,语义上更加自然(一个矩阵只有一类元素)。

2. SimpleMatrix

底层存储 Vec<Vec> ,二维向量。

  • rows() :外层 vector 的长度;
  • cols() :第一行的长度;空矩阵返回0。

3. get / set

通过索引直接读写矩阵元素;本题简化版,没有边界检查(越界会panic)。工业代码需要做越界判断,返回 Result 。

4. Display 格式化矩阵

遍历每一行,每行元素转字符串、空格拼接,每行末尾 writeln! 输出换行。

5. transpose 转置函数

  • 新建一个 c × r 的二维数组,初始值为 Default;
  • 双重循环: transposed_dataji = matrix.get(i,j) ,行列下标互换;
  • 构造并返回新的 SimpleMatrix 。

约束条件解释

rust 复制代码
  
fn transpose<M: Matrix>(matrix:&M)->SimpleMatrix<M::Element>
where M::Element: Copy + Default
 

M::Element 已经在Matrix trait上有一系列约束;但是构造SimpleMatrix需要 Copy + Default ,所以where子句再次补充约束。

相关推荐
水巷石子1 小时前
备考系统架构设计师第一天
学习·架构·软考·设计·考试
新时代牛马1 小时前
字符设备驱动完整篇:从 cdev_add、file_operations 到chrdev_open 与排障
开发语言·python
swordbob1 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言
是隼人2 小时前
buuctf-pwn inndy_echo(32位fmt)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
白山编程大哥2 小时前
Java OutputStreamWriter 详解:从字符到字节的桥梁
java·开发语言·python
xian_wwq3 小时前
【学习笔记】深度认知系列-第16讲-提示词工程
人工智能·笔记·学习
洛阳纸贵3 小时前
MATLAB-matlab基础知识
学习·算法·matlab
shmily麻瓜小菜鸡3 小时前
JavaScript / TypeScript 易踩坑知识点 —— 异步编程类
开发语言·javascript·typescript
Vcaker3 小时前
Linux学习25-harbor私有仓库部署
linux·运维·学习