Rust 基础学习

在 Rust 中,struct(结构体)和 trait(特征)是两个核心概念,分别用于数据封装和行为抽象,是构建 Rust 程序的基础。

在 Rust 中,struct(结构体)和 trait(特征)是两个核心概念,分别用于数据封装行为抽象,是构建 Rust 程序的基础。

1. struct(结构体):自定义数据类型

struct 用于将多个相关的数据字段组合成一个自定义类型,类似于其他语言中的 “结构体” 或简化版的 “类”(但不直接包含方法,方法需通过 impl 块实现)。

核心作用:

  • 封装相关数据,将零散的字段组织成有意义的整体。

  • 定义自定义类型,使代码更具可读性和可维护性。

基本语法与示例:

// 定义一个结构体(包含多个字段)
struct Person {
    name: String,  // 名字(String类型)
    age: u32,      // 年龄(无符号32位整数)
    is_student: bool,  // 是否为学生(布尔值)
}

// 通过 impl 块为结构体实现方法
impl Person {
    // 关联函数(类似“构造函数”):创建Person实例
    fn new(name: String, age: u32, is_student: bool) -> Self {
        Person { name, age, is_student }
    }

    // 实例方法:打印个人信息
    fn print_info(&self) {
        println!("Name: {}, Age: {}, Student: {}", self.name, self.age, self.is_student);
    }

    // 可变方法:修改年龄
    fn set_age(&mut self, new_age: u32) {
        self.age = new_age;
    }
}

fn main() {
    // 创建结构体实例
    let mut alice = Person::new("Alice".to_string(), 20, true);
    
    // 调用方法
    alice.print_info();  // 输出:Name: Alice, Age: 20, Student: true
    alice.set_age(21);
    alice.print_info();  // 输出:Name: Alice, Age: 21, Student: true
}

常见使用场景:

  • 表示实体(如用户、商品、坐标等),将其属性封装成一个类型。

  • 组织复杂数据(如配置项、日志信息等)。

  • 作为函数参数或返回值,传递结构化数据。

2. trait(特征):行为的抽象与约束

trait 用于定义一组方法的集合(即 “行为”),类似于其他语言中的 “接口(interface)”。它不提供方法的具体实现(可提供默认实现),而是规定:任何实现了该 trait 的类型,必须具备这些方法

核心作用:

  • 抽象不同类型的共同行为(如 “可打印”“可比较” 等)。

  • 实现多态:同一 trait 的不同实现可以通过 trait 对象或泛型约束统一调用。

  • 作为泛型约束,限制泛型参数必须具备特定行为。

// 定义一个trait(包含方法签名)
trait Printable {
    // 必须实现的方法
    fn print(&self);
    
    // 可选:默认实现的方法(实现者可覆盖)
    fn print_with_prefix(&self, prefix: &str) {
        println!("{}: ", prefix);
        self.print();  // 调用必须实现的print方法
    }
}

// 让之前的Person结构体实现Printable trait
impl Printable for Person {
    // 实现trait中必须的print方法
    fn print(&self) {
        println!("Person: {} (Age: {})", self.name, self.age);
    }
}

// 再定义一个结构体,并实现Printable trait
struct Book {
    title: String,
    author: String,
}

impl Printable for Book {
    fn print(&self) {
        println!("Book: '{}' by {}", self.title, self.author);
    }
}

fn main() {
    let alice = Person::new("Alice".to_string(), 21, true);
    let book = Book {
        title: "Rust 入门".to_string(),
        author: "张三".to_string(),
    };

    // 调用trait方法(多态:不同类型调用同一方法,行为不同)
    alice.print();  // 输出:Person: Alice (Age: 21)
    book.print();   // 输出:Book: 'Rust 入门' by 张三

    // 调用默认实现的方法
    alice.print_with_prefix("用户信息");  // 输出:用户信息: Person: Alice (Age: 21)
}

常见使用场景:

  • 定义通用行为(如 ToString trait 规定 “可转换为字符串”,Debug trait 规定 “可调试打印”)。

  • 作为函数参数,限制参数必须具备特定能力(如 fn log(item: &impl Printable) 要求参数可打印)。

  • 实现多态设计(如通过 trait 对象 &dyn Printable 统一处理不同类型的实例)。

总结:structtrait 的关系

  • struct 是 “数据的载体”:用于封装具体数据,是程序中 “实体” 的抽象。

  • trait 是 “行为的契约”:用于定义抽象行为,是程序中 “能力” 的抽象。

  • 两者结合:一个 struct 可以实现多个 trait(获得多种行为),一个 trait 可以被多个 struct 实现(不同实体共享同一行为),这是 Rust 代码复用和抽象的核心方式。

  • struct 像 “人”:是一个具体的 “实体”,包含自身的 “属性”(数据字段)。比如一个 Person 结构体可能有 nameage 等字段,就像人有姓名、年龄等特征。

  • trait 像 “技能包”:定义了一组 “行为能力”(方法),但不绑定具体实体。比如 PlayBasketball 这个 trait 可能包含 shoot()dribble() 等方法,就像 “打篮球” 这个技能包规定了 “投篮”“运球” 等动作。

  • 技能的实现方式

    • 有些技能包(trait)自带 “基础操作指南”(默认方法),比如 PlayBasketball 可能默认实现了 warm_up() 方法(热身动作),任何 “学会这个技能” 的人(实现该 trait 的 struct)都可以直接用。

    • 但核心技能(trait 中未提供默认实现的方法)需要自己练,比如 shoot() 必须由具体的人(struct)根据自身特点实现(姚明和普通人的投篮方式不同)。

  • 多技能与多实体

    • 一个人(struct)可以同时掌握多个技能包(实现多个 trait),比如既会打篮球(PlayBasketball)又会编程(Programmable)。

    • 一个技能包(trait)也可以被多个人(不同 struct)掌握,比如 PlayBasketball 可以被 PersonRobot(机器人结构体)同时实现。

这个比喻完美体现了 Rust 中 “数据(struct)与行为(trait)分离” 的设计思想 —— 通过 trait 抽象行为,通过 struct 承载数据,再通过 impl Trait for Struct 将两者结合,既保证了灵活性,又实现了代码复用。

LICENSED UNDER CC BY-NC-SA 4.0
评论