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};

这表示 user2email 是新的,其他字段都从 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);

即便内部数据一样,blackorigin 也是不同类型,不能混用。

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)的基石。


练习题

  1. 定义一个 Rectangle 结构体,实现一个方法 can_hold(&self, other: &Rectangle) -> bool,判断当前矩形是否能容纳另一个矩形。
  2. 尝试创建一个结构体,包含一个字符串引用的字段。看看编译器会报什么错?(这涉及到生命周期,我们后面会讲)。

思考题

Go 语言通过组合 (Composition) 和接口 (Interface) 来实现复用和多态。Rust 的 struct 和 impl 看起来更像 OO 中的类,但 Rust 本身并不是典型的 OOP 语言,为什么?


本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读