第三篇: Rust 结构体、Trait 和方法详解

Rust 结构体、Trait 和方法详解

Rust 没有传统的"类"和"接口"概念,而是使用结构体(Struct)和 Trait 来实现面向对象编程。

目录

  1. 结构体 (Struct)
  2. 方法和关联函数
  3. Trait(类似接口)
  4. Trait 对象与多态
  5. 泛型与约束
  6. 实战示例

结构体 (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__

关键区别

  1. 没有类继承:Rust 使用组合优于继承的设计理念
  2. Trait 比接口强大:可以有默认实现、关联类型、泛型参数
  3. 零成本抽象:泛型编译时单态化,无运行时开销
  4. 所有权系统 :方法的 self 参数控制所有权转移

最佳实践

  1. 优先使用 &self 而不是 self(避免所有权转移)
  2. 使用 impl Trait 简化函数签名
  3. 小 Trait 优于大 Trait(单一职责)
  4. 使用 derive 宏自动实现标准 Trait
  5. 泛型优于 Trait 对象(性能更好)
  6. 需要运行时多态时才用 Trait 对象

延伸阅读

相关推荐
John_Rey2 小时前
Rust底层深度探究:自定义分配器(Allocators)——控制内存分配的精妙艺术
开发语言·后端·rust
isyuah2 小时前
Rust Miko 框架系列(二):快速上手与基础示例
后端·rust
isyuah2 小时前
Rust Miko 框架系列(四):深入路由系统
后端·rust
星释5 小时前
Rust 练习册 10:多线程基础与并发安全
开发语言·后端·rust
2401_8603195212 小时前
【无标题】
开发语言·学习·rust
微小冷13 小时前
Rust实战教程:做一个UDP聊天软件
rust·udp·egui·聊天软件·rust教程·用户图形界面
星释16 小时前
Rust 练习册 :Rail Fence Cipher与栅栏密码
开发语言·算法·rust
Source.Liu18 小时前
【Chrono】Cargo.toml 配置文件深度分析
rust·time
shykevin21 小时前
Rust入门
开发语言·后端·rust