Rust 结构体(长文讲解)

什么是 Rust 结构体?从生活中的“模板”说起

在现实世界里,我们经常用“模板”来组织信息。比如一份请假条,它有固定的格式:姓名、部门、请假类型、起止时间、事由等。这些字段组合在一起,就构成了一条完整的请假记录。Rust 结构体(Struct)正是编程世界中的“模板”,它允许我们把多个相关数据打包成一个整体,方便管理与使用。

你可以把结构体想象成一个自定义的数据容器,它不像基本类型那样只能存一个值(比如整数或布尔值),而是能同时保存多个不同类型的数据。比如我们要描述一个人,可以包含姓名(字符串)、年龄(整数)、身高(浮点数)等信息。这些信息组合起来,就构成了一个完整的“人”的数据模型。

Rust 结构体最强大的地方在于:它不仅是数据的集合,还自带行为(方法),是面向对象思想在 Rust 中的重要体现。尤其在构建复杂系统时,结构体让代码更清晰、更安全,避免了数据错乱或访问错误的问题。


定义与使用结构体:从零开始创建你的第一个数据模型

让我们从一个最简单的例子开始。假设我们要记录一个学生的成绩信息,包含姓名、年龄和成绩。

// 定义一个名为 Student 的结构体
struct Student {
    name: String,      // 姓名,使用 String 类型存储可变字符串
    age: u8,           // 年龄,使用无符号 8 位整数,范围 0 到 255
    score: f32,        // 成绩,使用 32 位浮点数,支持小数
}

这段代码定义了一个 Student 结构体,它有三个字段:nameagescore。注意,每个字段都必须指定类型,这是 Rust 类型安全机制的一部分。

接下来,我们创建一个 Student 实例:

fn main() {
    // 创建一个 Student 实例
    let student1 = Student {
        name: String::from("张三"),   // 使用 String::from 创建字符串
        age: 18,                     // 年龄直接赋值
        score: 95.5,                 // 成绩为浮点数
    };

    // 打印学生信息
    println!("姓名: {}, 年龄: {}, 成绩: {:.1}", student1.name, student1.age, student1.score);
}

输出结果:

姓名: 张三, 年龄: 18, 成绩: 95.5

这里的关键是:结构体实例的创建语法是大括号 {},字段名与值用冒号 : 连接。你可以自由调整字段顺序,只要名字对应即可,这提高了代码的可读性。

💡 小提示:String::from() 是创建字符串的常用方法,相比字面量 &str,它拥有所有权,适合在函数间传递。


结构体字段的访问与修改:就像打开抽屉取东西

一旦你创建了结构体实例,接下来最自然的需求就是“读取”和“修改”它的字段。在 Rust 中,这通过点号 . 实现。

fn main() {
    let mut student = Student {
        name: String::from("李四"),
        age: 20,
        score: 88.0,
    };

    // 读取字段
    println!("当前姓名: {}", student.name);

    // 修改字段(注意:必须用 mut 修饰变量)
    student.age = 21;
    student.score = 92.0;

    println!("更新后:姓名: {}, 年龄: {}, 成绩: {:.1}", student.name, student.age, student.score);
}

输出:

当前姓名: 李四
更新后:姓名: 李四, 年龄: 21, 成绩: 92.0

这里有个重要细节:只有用 mut 声明的变量才能被修改。Rust 默认是不可变的,这是为了防止意外修改数据,提高程序安全性。

这就像你家的抽屉,有些抽屉是锁住的(不可变),有些是打开的(可变)。只有你明确告诉系统“我需要修改”,它才允许你打开抽屉并放入新东西。


使用结构体方法:让数据“会说话”

结构体不仅仅是数据的容器,它还能“说话”——通过方法(methods)实现行为。方法是与结构体相关联的函数,可以操作结构体的数据。

我们为 Student 添加一个判断是否及格的方法:

impl Student {
    // 定义一个方法,判断成绩是否及格(>= 60)
    fn is_pass(&self) -> bool {
        self.score >= 60.0
    }

    // 另一个方法:打印完整信息
    fn print_info(&self) {
        println!("学生: {}, 年龄: {}, 成绩: {:.1},是否及格: {}", 
                 self.name, self.age, self.score, self.is_pass());
    }
}

📌 impl 关键字用于实现结构体的方法。&self 表示方法借用当前结构体实例,不获取所有权,适合只读操作。

使用方法如下:

fn main() {
    let student = Student {
        name: String::from("王五"),
        age: 19,
        score: 58.0,
    };

    student.print_info();  // 输出:学生: 王五, 年龄: 19, 成绩: 58.0,是否及格: false
}

通过方法,我们把“判断及格”这个逻辑封装在结构体内,避免了在主函数中写重复的判断代码。这正是“封装”的魅力所在:数据与行为绑定在一起,代码更清晰、更易维护


结构体的实用案例:管理图书信息

让我们用一个更复杂的例子来展示 Rust 结构体的强大。假设我们要设计一个图书管理系统,每本书包含书名、作者、出版年份和是否借出。

struct Book {
    title: String,
    author: String,
    year: u16,
    is_borrowed: bool,
}

// 为 Book 实现方法
impl Book {
    // 创建新书的构造函数(推荐方式)
    fn new(title: &str, author: &str, year: u16) -> Book {
        Book {
            title: title.to_string(),
            author: author.to_string(),
            year,
            is_borrowed: false,
        }
    }

    // 借书方法
    fn borrow(&mut self) {
        if self.is_borrowed {
            println!("抱歉,这本书已被借出!");
        } else {
            self.is_borrowed = true;
            println!("借书成功:《{}》", self.title);
        }
    }

    // 还书方法
    fn return_book(&mut self) {
        if !self.is_borrowed {
            println!("这本书没有被借出!");
        } else {
            self.is_borrowed = false;
            println!("还书成功:《{}》", self.title);
        }
    }

    // 打印书籍信息
    fn display(&self) {
        println!("书名: {}, 作者: {}, 出版年份: {}, 状态: {}", 
                 self.title, self.author, self.year, 
                 if self.is_borrowed { "已借出" } else { "可借" });
    }
}

使用示例:

fn main() {
    let mut book = Book::new("Rust 编程之道", "张明", 2023);

    book.display();       // 输出:书名: Rust 编程之道, 作者: 张明, 出版年份: 2023, 状态: 可借
    book.borrow();        // 借书成功:《Rust 编程之道》
    book.borrow();        // 抱歉,这本书已被借出!
    book.return_book();   // 还书成功:《Rust 编程之道》
    book.display();       // 输出:书名: Rust 编程之道, 作者: 张明, 出版年份: 2023, 状态: 可借
}

这个例子展示了结构体如何组织现实世界的复杂信息。通过构造函数 new,我们能统一创建对象的方式;通过方法,我们封装了业务逻辑。整个系统结构清晰,扩展性强。


结构体的进阶技巧:字段名简写与结构体更新语法

在实际开发中,我们常常需要创建一个结构体实例,然后只修改其中少数几个字段。Rust 提供了“结构体更新语法”,可以避免重复书写所有字段。

fn main() {
    let book1 = Book {
        title: String::from("深入理解 Rust"),
        author: String::from("李华"),
        year: 2022,
        is_borrowed: false,
    };

    // 使用结构体更新语法:只修改 title
    let book2 = Book {
        title: String::from("Rust 高级编程"),
        ..book1  // 其他字段从 book1 复制过来
    };

    println!("新书: {}", book2.title); // 输出:Rust 高级编程
    println!("原书作者: {}", book1.author); // 输出:李华
}

🌟 注意:.. 表示“复制其余字段”,但必须放在最后。这个语法特别适合在原型基础上快速创建变体。

此外,Rust 还支持“字段名简写”(field init shorthand),当变量名与字段名相同时,可以省略重复书写。

fn create_book(title: String, author: String, year: u16) -> Book {
    Book {
        title,   // 等价于 title: title
        author,  // 等价于 author: author
        year,
        is_borrowed: false,
    }
}

这大大减少了冗余代码,让结构体定义更简洁。


总结:Rust 结构体,让数据更有组织力

Rust 结构体是 Rust 语言中最重要的数据抽象工具之一。它不仅仅是“数据的盒子”,更是组织逻辑、封装行为、提升代码可读性的核心机制。

从最简单的学生信息,到复杂的图书管理系统,结构体都能胜任。它帮助我们把零散的数据变成有意义的整体,让程序更安全、更可维护。

掌握结构体,就是掌握 Rust 编程的“骨架”。当你能熟练定义、使用、扩展结构体时,你就真正迈入了 Rust 的核心世界。

无论你是初学者,还是已有其他语言经验的开发者,都建议你从结构体开始深入 Rust。它不会让你失望。