Rust 学习笔记 08:结构体 (Structs)
“Structure is the maker of light.” – Louis Kahn
在 Go 中,我们用 struct 来组织数据,用 func (s *Struct) Method() 来定义方法。
在 Rust 中,这种感觉非常相似,但细节决定成败。
1. 定义与实例化
定义一个 User 结构体:
1struct User {
2 active: bool,
3 username: String,
4 email: String,
5 sign_in_count: u64,
6}
这和 Go 几乎一模一样(除了字段名为 snake_case,Go 推荐 CamelCase)。
实例化:
1let user1 = User {
2 email: String::from("someone@example.com"),
3 username: String::from("someusername123"),
4 active: true,
5 sign_in_count: 1,
6};
区别点:如果想修改 user1 的字段,必须把 user1 声明为 mut。Rust 不允许仅把某个字段标记为可变,可变性是针对整个实例的。
2. 字段初始化简写
如果变量名和字段名一样,可以简写,这对 Go 开发者来说很亲切(类似 {Key: Value} 简写):
1fn build_user(email: String, username: String) -> User {
2 User {
3 email, // 等同于 email: email
4 username,
5 active: true,
6 sign_in_count: 1,
7 }
8}
3. 结构体更新语法 (..user1)
这是一个非常酷的特性,Go 没有原生支持(通常需要手动拷贝)。
1let user2 = User {
2 email: String::from("another@example.com"),
3 ..user1
4};
这表示 user2 的 email 是新的,其他字段都从 user1 拿。
坑点预警:这仍然遵循 Move 语义!
如果 user1 中的某些字段(如 String)被移动到了 user2,那么 user1 以后就不能完整使用了。
4. 元组结构体 (Tuple Structs)
当你只想给一个元组起个名字时使用。
1struct Color(i32, i32, i32);
2struct Point(i32, i32, i32);
3
4let black = Color(0, 0, 0);
5let origin = Point(0, 0, 0);
即便内部数据一样,black 和 origin 也是不同类型,不能混用。
5. 方法 (Methods)
Rust 没有 Go 那样的接收者语法 func (s User) Name(),而是使用了 impl 块。
1struct Rectangle {
2 width: u32,
3 height: u32,
4}
5
6impl Rectangle {
7 // &self 是 self: &Self 的语法糖
8 fn area(&self) -> u32 {
9 self.width * self.height
10 }
11}
调用时:rect1.area()。
Rust 有一个自动解引用 (Automatic Dereferencing) 功能,所以我们调用方法时不需要像 C++ 那样区分 . 和 ->,大部分时候也不用手动写 (&rect1).area(),编译器会自动识别是否需要传引用。
6. 关联函数 (Associated Functions)
如果在 impl 块里定义的函数不以 self 为第一参数,那它就是关联函数(类似静态方法)。最常见的就是 String::from。
我们通常用它来实现构造函数(因为 Rust 没有 new 关键字):
1impl Rectangle {
2 fn square(size: u32) -> Self {
3 Self {
4 width: size,
5 height: size,
6 }
7 }
8}
9
10let sq = Rectangle::square(3);
7. 小结
第八篇笔记,我们掌握了 Rust 的数据建模工具:
struct定义数据。impl块定义行为(方法)。- 注意
struct update syntax可能带来的所有权转移问题。
下一篇,我们将看看 Rust 的枚举 Enum。
别以为它只是简单的枚举值,Rust 的 Enum 是代数数据类型,它能做的事情远超 Go 的 iota 常量组,它是 Rust “安全处理空值”(Option)和 “结果处理”(Result)的基石。
练习题:
- 定义一个
Rectangle结构体,实现一个方法can_hold(&self, other: &Rectangle) -> bool,判断当前矩形是否能容纳另一个矩形。 - 尝试创建一个结构体,包含一个字符串引用的字段。看看编译器会报什么错?(这涉及到生命周期,我们后面会讲)。
思考题:
Go 语言通过组合 (Composition) 和接口 (Interface) 来实现复用和多态。Rust 的 struct 和 impl 看起来更像 OO 中的类,但 Rust 本身并不是典型的 OOP 语言,为什么?
本文代码示例:
关注公众号:极客老墨
更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。
