Trait 与泛型
这篇讲 Rust 中 trait 和泛型的配合使用。Trait 类似于 Go 的 interface 和 Java 的 interface 的结合体——但更强大,因为它支持默认实现和关联类型。泛型则让代码复用的同时保持类型安全。
Trait:定义共享行为
Trait 告诉编译器:「实现了这个 trait 的类型,一定具备这些方法」。
定义 Trait
pub trait Summary {
fn summarize(&self) -> String; // 只有签名,没有实现(类似 Go interface)
// 也可以提供默认实现
fn default_summary(&self) -> String {
String::from("(Read more...)")
}
}为类型实现 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)
}
// default_summary 使用默认实现,无需重写
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}孤儿规则:要实现某个 trait 于某个类型,trait 或类型中至少有一个是在本 crate 中定义的。你不能为外部类型实现外部 trait——这防止了两个 crate 对同一类型的不同实现。
Trait 作为参数
// 写法 1:impl Trait 语法糖
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// 写法 2:trait bound——等价的长写法
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// 多个参数、多个 trait bound
pub fn notify_and_display<T: Summary + Display>(item: &T, other: &T) {
// ...
}
// where 从句让复杂 bound 更可读
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
// ...
}返回实现了 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,
}
}impl Trait 作为返回值时,函数只能返回同一种具体类型。如果需要返回不同类型(如有时返回 NewsArticle,有时返回 Tweet),需要用 Box<dyn Trait>(trait 对象)。泛型
泛型让函数和结构体能处理多种类型而不损失类型安全:
// 泛型函数
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
// 泛型结构体
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// 对特定类型的方法
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("最大的是 {result}"); // 100
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}常用派生 Trait
通过 #[derive] 属性自动生成常见 trait 的实现:
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
struct MyStruct {
field1: i32,
field2: String,
}
// Debug: 可以用 {:?} 打印
// Clone: .clone() 深度复制
// PartialEq / Eq: 可以用 == 比较
// Hash: 可以作为 HashMap 的 key
// Default: Default::default() 创建默认值
Trait 与泛型的组合——标准库实例
标准库中泛型 + trait 的经典例子是 HashMap<K, V>。K 必须是可哈希的,V 是任意值——这在 Go 中对应 map 的类型约束:
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
}一句话小结
Trait 定义共享行为(类似 Go interface),泛型让代码复用。impl Trait 语法糖简洁,trait bound 精确。#[derive] 自动生成常见 trait。「组合优于继承」——Rust 没有类继承,只有 trait + 组合。下一篇讲 集合类型。
练习
- 定义一个
Areatrait,有一个方法fn area(&self) -> f64。为Circle和Rectangle结构体实现这个 trait。 - 写一个泛型函数
pair_equal<T: PartialEq>(a: T, b: T) -> bool,返回两个值是否相等。
参考答案
use std::f64::consts::PI;
trait Area {
fn area(&self) -> f64;
}
struct Circle {
radius: f64,
}
impl Area for Circle {
fn area(&self) -> f64 {
PI * self.radius * self.radius
}
}
struct Rectangle {
width: f64,
height: f64,
}
impl Area for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
fn pair_equal<T: PartialEq>(a: T, b: T) -> bool {
a == b
}
fn main() {
let c = Circle { radius: 5.0 };
let r = Rectangle { width: 4.0, height: 6.0 };
println!("圆面积: {:.2}, 矩形面积: {:.2}", c.area(), r.area());
println!("相等吗?{}", pair_equal(42, 42)); // true
println!("相等吗?{}", pair_equal("hello", "world")); // false
}最后更新于