Rust 结构体、Trait 和方法详解
Rust 没有传统的"类"和"接口"概念,而是使用结构体(Struct)和 Trait 来实现面向对象编程。
目录
结构体 (Struct)
结构体是 Rust 中自定义数据类型的主要方式,类似于其他语言的"类",但只包含数据,不包含方法。
1. 命名结构体(Named Struct)
最常用的结构体形式,每个字段都有名称。
rust
// 定义一个用户结构体
struct User {
id: i64,
username: String,
email: String,
active: bool,
}
fn main() {
// 创建实例
let user1 = User {
id: 1,
username: String::from("alice"),
email: String::from("alice@example.com"),
active: true,
};
// 访问字段
println!("用户名: {}", user1.username);
println!("邮箱: {}", user1.email);
// 可变实例(整个实例必须可变)
let mut user2 = User {
id: 2,
username: String::from("bob"),
email: String::from("bob@example.com"),
active: false,
};
// 修改字段
user2.active = true;
println!("用户 {} 已激活", user2.username);
}
2. 元组结构体(Tuple Struct)
没有字段名,只有字段类型,通过索引访问。
rust
// 定义颜色结构体
struct Color(i32, i32, i32);
// 定义点结构体
struct Point(f64, f64);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0);
// 通过索引访问
println!("黑色 RGB: ({}, {}, {})", black.0, black.1, black.2);
println!("原点坐标: ({}, {})", origin.0, origin.1);
// 解构
let Color(r, g, b) = black;
println!("红色分量: {}", r);
}
3. 单元结构体(Unit-like Struct)
没有任何字段,常用于实现 Trait。
csharp
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
4. 结构体字段简写
当变量名与字段名相同时可以简写。
rust
struct User {
username: String,
email: String,
}
fn create_user(username: String, email: String) -> User {
// 字段简写
User {
username, // 等同于 username: username
email, // 等同于 email: email
}
}
fn main() {
let user = create_user(
String::from("charlie"),
String::from("charlie@example.com")
);
println!("{}", user.username);
}
5. 结构体更新语法
从其他实例创建新实例,类似于"继承"部分字段。
rust
struct User {
username: String,
email: String,
active: bool,
sign_in_count: u64,
}
fn main() {
let user1 = User {
username: String::from("user1"),
email: String::from("user1@example.com"),
active: true,
sign_in_count: 1,
};
// 基于 user1 创建 user2,只修改部分字段
let user2 = User {
username: String::from("user2"),
email: String::from("user2@example.com"),
..user1 // 其余字段从 user1 复制(移动)
};
println!("user2 活跃状态: {}", user2.active);
// 注意:user1 的 username 和 email 已被移动,不能再使用
}
方法和关联函数
使用 impl 块为结构体定义方法和关联函数。
1. 实例方法(Methods)
第一个参数是 self(或 &self、&mut self),通过实例调用。
rust
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 不可变借用方法
fn area(&self) -> u32 {
self.width * self.height
}
// 可变借用方法
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
// 获取所有权的方法(较少使用)
fn destroy(self) {
println!("矩形被销毁: {}x{}", self.width, self.height);
}
// 带参数的方法
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("面积: {}", rect1.area());
let mut rect2 = Rectangle { width: 10, height: 20 };
rect2.scale(2);
println!("缩放后: {}x{}", rect2.width, rect2.height);
let rect3 = Rectangle { width: 5, height: 10 };
println!("rect2 能容纳 rect3 吗?{}", rect2.can_hold(&rect3));
// 获取所有权的方法
rect1.destroy();
// rect1 已失效,不能再使用
}
2. 关联函数(Associated Functions)
没有 self 参数,类似于其他语言的"静态方法",通过类型名调用。
rust
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 关联函数(构造器)
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
// 工厂方法
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
// 实例方法
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
// 使用关联函数创建实例
let rect = Rectangle::new(30, 50);
let square = Rectangle::square(25);
println!("矩形面积: {}", rect.area());
println!("正方形面积: {}", square.area());
}
3. 多个 impl 块
可以为同一类型定义多个 impl 块。
rust
struct User {
name: String,
age: u32,
}
impl User {
fn new(name: String, age: u32) -> Self {
Self { name, age }
}
}
impl User {
fn greet(&self) {
println!("你好,我是 {},今年 {} 岁", self.name, self.age);
}
fn birthday(&mut self) {
self.age += 1;
println!("生日快乐!现在 {} 岁了", self.age);
}
}
fn main() {
let mut user = User::new(String::from("Alice"), 25);
user.greet();
user.birthday();
}
Trait(类似接口)
Trait 定义共享行为的接口,类似于其他语言的"接口"。
1. 定义和实现 Trait
rust
// 定义 Trait
trait Summary {
// 方法签名(需要实现)
fn summarize(&self) -> String;
// 默认实现
fn notify(&self) {
println!("通知: {}", self.summarize());
}
}
// 为结构体实现 Trait
struct Article {
title: String,
author: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("《{}》 作者: {}", self.title, self.author)
}
// 可以覆盖默认实现
fn notify(&self) {
println!("📰 新文章: {}", self.summarize());
}
}
struct Tweet {
username: String,
content: String,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("@{}: {}", self.username, self.content)
}
// 使用默认的 notify 实现
}
fn main() {
let article = Article {
title: String::from("Rust 编程"),
author: String::from("张三"),
content: String::from("Rust 是一门系统编程语言..."),
};
let tweet = Tweet {
username: String::from("alice"),
content: String::from("今天学习了 Rust Trait!"),
};
println!("{}", article.summarize());
article.notify();
println!("{}", tweet.summarize());
tweet.notify();
}
2. Trait 作为参数
rust
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
self.title.clone()
}
}
// 方式一:impl Trait 语法
fn notify1(item: &impl Summary) {
println!("通知: {}", item.summarize());
}
// 方式二:Trait 约束语法(更灵活)
fn notify2<T: Summary>(item: &T) {
println!("通知: {}", item.summarize());
}
// 多个 Trait 约束
fn notify3<T: Summary + Clone>(item: &T) {
println!("通知: {}", item.summarize());
}
// where 子句(约束较多时更清晰)
fn notify4<T>(item: &T)
where
T: Summary + Clone,
{
println!("通知: {}", item.summarize());
}
fn main() {
let article = Article {
title: String::from("标题"),
};
notify1(&article);
notify2(&article);
}
3. Trait 作为返回值
rust
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
self.title.clone()
}
}
// 返回实现了 Summary 的类型
fn create_summary(is_article: bool) -> impl Summary {
Article {
title: String::from("新闻标题"),
}
}
fn main() {
let summary = create_summary(true);
println!("{}", summary.summarize());
}
4. 常用标准库 Trait
Clone - 显式复制
rust
#[derive(Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone(); // 显式克隆
println!("p1: ({}, {})", p1.x, p1.y);
println!("p2: ({}, {})", p2.x, p2.y);
}
Copy - 隐式复制
rust
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 自动复制(不是移动)
println!("p1: ({}, {})", p1.x, p1.y);
println!("p2: ({}, {})", p2.x, p2.y);
}
Debug - 调试输出
rust
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
fn main() {
let user = User {
name: String::from("Alice"),
age: 25,
};
println!("{:?}", user); // User { name: "Alice", age: 25 }
println!("{:#?}", user); // 美化输出
}
PartialEq 和 Eq - 相等比较
rust
#[derive(PartialEq, Eq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
let p3 = Point { x: 3, y: 4 };
println!("p1 == p2: {}", p1 == p2); // true
println!("p1 == p3: {}", p1 == p3); // false
}
Trait 对象与多态
Trait 对象允许运行时多态。
1. 使用 Trait 对象
rust
trait Draw {
fn draw(&self);
}
struct Circle {
radius: f64,
}
impl Draw for Circle {
fn draw(&self) {
println!("绘制圆形,半径: {}", self.radius);
}
}
struct Rectangle {
width: f64,
height: f64,
}
impl Draw for Rectangle {
fn draw(&self) {
println!("绘制矩形,宽: {}, 高: {}", self.width, self.height);
}
}
fn main() {
// 使用 Box<dyn Trait> 存储不同类型
let shapes: Vec<Box<dyn Draw>> = vec![
Box::new(Circle { radius: 5.0 }),
Box::new(Rectangle { width: 10.0, height: 20.0 }),
];
// 动态分发
for shape in shapes.iter() {
shape.draw();
}
}
2. 引用形式的 Trait 对象
rust
trait Animal {
fn make_sound(&self);
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn make_sound(&self) {
println!("汪汪!");
}
}
impl Animal for Cat {
fn make_sound(&self) {
println!("喵喵!");
}
}
fn animal_sound(animal: &dyn Animal) {
animal.make_sound();
}
fn main() {
let dog = Dog;
let cat = Cat;
animal_sound(&dog);
animal_sound(&cat);
}
泛型与约束
泛型允许编写适用于多种类型的代码。
1. 泛型结构体
rust
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 为特定类型实现方法
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let int_point = Point::new(5, 10);
let float_point = Point::new(1.0, 4.0);
println!("距离原点: {}", float_point.distance_from_origin());
}
2. 多个泛型参数
csharp
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}
3. Trait 约束
rust
use std::fmt::Display;
struct Pair<T> {
first: T,
second: T,
}
// 无约束的实现
impl<T> Pair<T> {
fn new(first: T, second: T) -> Self {
Self { first, second }
}
}
// 带 Trait 约束的实现
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.first >= self.second {
println!("最大值: {}", self.first);
} else {
println!("最大值: {}", self.second);
}
}
}
fn main() {
let pair = Pair::new(10, 20);
pair.cmp_display();
}
实战示例
示例 1:完整的用户系统
rust
use std::fmt;
// 定义 Trait
trait Authenticate {
fn check_password(&self, password: &str) -> bool;
fn is_active(&self) -> bool;
}
trait Display {
fn display(&self) -> String;
}
// 用户结构体
#[derive(Debug, Clone)]
struct User {
id: u64,
username: String,
password_hash: String,
email: String,
active: bool,
}
impl User {
// 关联函数(构造器)
fn new(username: String, password: String, email: String) -> Self {
Self {
id: 0,
username,
password_hash: format!("hashed_{}", password),
email,
active: true,
}
}
// 实例方法
fn set_id(&mut self, id: u64) {
self.id = id;
}
fn deactivate(&mut self) {
self.active = false;
}
fn activate(&mut self) {
self.active = true;
}
}
// 实现 Trait
impl Authenticate for User {
fn check_password(&self, password: &str) -> bool {
self.password_hash == format!("hashed_{}", password)
}
fn is_active(&self) -> bool {
self.active
}
}
impl Display for User {
fn display(&self) -> String {
format!(
"User #{}: {} ({})",
self.id,
self.username,
if self.active { "激活" } else { "未激活" }
)
}
}
// 实现标准库 Trait
impl fmt::Display for User {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.display())
}
}
// 泛型函数
fn authenticate<T: Authenticate>(user: &T, password: &str) -> bool {
user.is_active() && user.check_password(password)
}
fn main() {
let mut user = User::new(
String::from("alice"),
String::from("secret123"),
String::from("alice@example.com"),
);
user.set_id(1);
println!("{}", user);
println!("密码正确: {}", authenticate(&user, "secret123"));
println!("密码错误: {}", authenticate(&user, "wrong"));
user.deactivate();
println!("未激活用户认证: {}", authenticate(&user, "secret123"));
}
输出:
bash
User #1: alice (激活)
密码正确: true
密码错误: false
未激活用户认证: false
示例 2:图形绘制系统
rust
use std::f64::consts::PI;
// 定义 Trait
trait Shape {
fn area(&self) -> f64;
fn perimeter(&self) -> f64;
fn describe(&self) -> String {
format!(
"面积: {:.2}, 周长: {:.2}",
self.area(),
self.perimeter()
)
}
}
trait Drawable {
fn draw(&self);
}
// 圆形
struct Circle {
radius: f64,
}
impl Circle {
fn new(radius: f64) -> Self {
Self { radius }
}
}
impl Shape for Circle {
fn area(&self) -> f64 {
PI * self.radius * self.radius
}
fn perimeter(&self) -> f64 {
2.0 * PI * self.radius
}
}
impl Drawable for Circle {
fn draw(&self) {
println!("○ 绘制圆形 (半径: {})", self.radius);
}
}
// 矩形
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
fn is_square(&self) -> bool {
self.width == self.height
}
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
}
impl Drawable for Rectangle {
fn draw(&self) {
let shape_type = if self.is_square() {
"正方形"
} else {
"矩形"
};
println!("□ 绘制{} (宽: {}, 高: {})", shape_type, self.width, self.height);
}
}
// 三角形
struct Triangle {
a: f64,
b: f64,
c: f64,
}
impl Triangle {
fn new(a: f64, b: f64, c: f64) -> Option<Self> {
// 验证三角形合法性
if a + b > c && b + c > a && c + a > b {
Some(Self { a, b, c })
} else {
None
}
}
}
impl Shape for Triangle {
fn area(&self) -> f64 {
// 海伦公式
let s = (self.a + self.b + self.c) / 2.0;
(s * (s - self.a) * (s - self.b) * (s - self.c)).sqrt()
}
fn perimeter(&self) -> f64 {
self.a + self.b + self.c
}
}
impl Drawable for Triangle {
fn draw(&self) {
println!("△ 绘制三角形 (边长: {}, {}, {})", self.a, self.b, self.c);
}
}
// 泛型函数:打印图形信息
fn print_shape_info<T: Shape + Drawable>(shape: &T) {
shape.draw();
println!(" {}", shape.describe());
}
// 使用 Trait 对象实现多态
fn draw_all(shapes: &[Box<dyn Drawable>]) {
for shape in shapes {
shape.draw();
}
}
fn main() {
let circle = Circle::new(5.0);
let rectangle = Rectangle::new(4.0, 6.0);
let square = Rectangle::new(5.0, 5.0);
let triangle = Triangle::new(3.0, 4.0, 5.0).unwrap();
println!("=== 使用泛型函数 ===");
print_shape_info(&circle);
println!();
print_shape_info(&rectangle);
println!();
print_shape_info(&square);
println!();
print_shape_info(&triangle);
println!("\n=== 使用 Trait 对象(多态)===");
let shapes: Vec<Box<dyn Drawable>> = vec![
Box::new(circle),
Box::new(rectangle),
Box::new(square),
Box::new(triangle),
];
draw_all(&shapes);
}
示例 3:数据库服务(模拟项目实际场景)
rust
use std::collections::HashMap;
// 定义 Repository Trait(类似接口)
trait Repository<T> {
fn find_by_id(&self, id: u64) -> Option<&T>;
fn find_all(&self) -> Vec<&T>;
fn save(&mut self, id: u64, entity: T) -> u64;
fn delete(&mut self, id: u64) -> bool;
}
// 用户实体
#[derive(Debug, Clone)]
struct User {
id: u64,
username: String,
email: String,
}
impl User {
fn new(id: u64, username: String, email: String) -> Self {
Self { id, username, email }
}
}
// 用户仓储实现
struct UserRepository {
storage: HashMap<u64, User>,
next_id: u64,
}
impl UserRepository {
fn new() -> Self {
Self {
storage: HashMap::new(),
next_id: 1,
}
}
}
impl Repository<User> for UserRepository {
fn find_by_id(&self, id: u64) -> Option<&User> {
self.storage.get(&id)
}
fn find_all(&self) -> Vec<&User> {
self.storage.values().collect()
}
fn save(&mut self, id: u64, mut entity: User) -> u64 {
let actual_id = if id == 0 {
let new_id = self.next_id;
self.next_id += 1;
entity.id = new_id;
new_id
} else {
entity.id = id;
id
};
self.storage.insert(actual_id, entity);
actual_id
}
fn delete(&mut self, id: u64) -> bool {
self.storage.remove(&id).is_some()
}
}
// 服务层
struct UserService<R: Repository<User>> {
repository: R,
}
impl<R: Repository<User>> UserService<R> {
fn new(repository: R) -> Self {
Self { repository }
}
fn create_user(&mut self, username: String, email: String) -> u64 {
let user = User::new(0, username, email);
self.repository.save(0, user)
}
fn get_user(&self, id: u64) -> Option<&User> {
self.repository.find_by_id(id)
}
fn list_users(&self) -> Vec<&User> {
self.repository.find_all()
}
fn delete_user(&mut self, id: u64) -> bool {
self.repository.delete(id)
}
}
fn main() {
let repo = UserRepository::new();
let mut service = UserService::new(repo);
println!("=== 用户管理系统 ===\n");
let id1 = service.create_user(
String::from("alice"),
String::from("alice@example.com"),
);
println!("创建用户 ID: {}", id1);
let id2 = service.create_user(
String::from("bob"),
String::from("bob@example.com"),
);
println!("创建用户 ID: {}", id2);
println!("\n所有用户:");
for user in service.list_users() {
println!(" {:?}", user);
}
println!("\n查询用户 ID=1:");
if let Some(user) = service.get_user(1) {
println!(" 找到: {:?}", user);
}
println!("\n删除用户 ID=1");
service.delete_user(1);
println!("\n删除后的所有用户:");
for user in service.list_users() {
println!(" {:?}", user);
}
}
总结对比
Rust vs 其他语言
| 概念 | Rust | Java/C# | Python |
|---|---|---|---|
| 类 | struct |
class |
class |
| 接口 | trait |
interface |
Protocol (3.8+) |
| 继承 | ❌ 使用组合和 Trait | extends |
class Child(Parent) |
| 多态 | Trait 对象 + 泛型 | 虚方法 | Duck Typing |
| 静态方法 | 关联函数 | static 方法 |
@staticmethod |
| 构造器 | 关联函数 new() |
new 关键字 |
__init__ |
关键区别
- 没有类继承:Rust 使用组合优于继承的设计理念
- Trait 比接口强大:可以有默认实现、关联类型、泛型参数
- 零成本抽象:泛型编译时单态化,无运行时开销
- 所有权系统 :方法的
self参数控制所有权转移
最佳实践
- 优先使用
&self而不是self(避免所有权转移) - 使用
impl Trait简化函数签名 - 小 Trait 优于大 Trait(单一职责)
- 使用
derive宏自动实现标准 Trait - 泛型优于 Trait 对象(性能更好)
- 需要运行时多态时才用 Trait 对象
延伸阅读
- The Rust Book - Structs
- The Rust Book - Traits
- Rust By Example - Traits
- 本项目中的实际应用:
src/models/,src/services/