知识点:关联类型
关联类型让 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子句再次补充约束。