Rust 中的结构体使用指南

1. 定义结构体(Struct)

定义一个结构体需要使用 struct 关键字,然后给该结构体取一个有意义的名字,接着在花括号内列出字段名称和字段类型。例如,下面的 User 结构体包含四个字段,用来表示用户的相关信息:

rust 复制代码
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

需要注意的是,结构体字段之间用逗号分隔,字段类型可以各不相同。字段名有助于我们理解各数据的含义,相比于元组,这种方式更具可读性和可维护性。

2. 实例化结构体

定义完结构体后,我们就可以基于它创建具体的实例。创建实例需要在结构体名称后面的花括号中填写 字段: 值。例如:

rust 复制代码
let user1 = User {
    active: true,
    username: String::from("someusername123"),
    email: String::from("someone@example.com"),
    sign_in_count: 1,
};

上述代码创建了一个名为 user1User 实例。

  • 访问字段时,使用点号(.)语法,比如 user1.email
  • 如果想要对结构体的字段进行修改,需要将整个实例声明为可变(mut),如下所示:
rust 复制代码
let mut user1 = User {
    active: true,
    username: String::from("someusername123"),
    email: String::from("someone@example.com"),
    sign_in_count: 1,
};

// 修改 email
user1.email = String::from("another@example.com");

Rust 不允许只将部分字段设为可变;只有把整个实例设为 mut,才能修改任意字段的值。

3. 返回结构体实例的函数

我们可以编写一个函数来返回一个结构体实例。以下例子中,build_user 函数接收用户名和邮箱,返回一个 User 实例:

rust 复制代码
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username: username,
        email: email,
        sign_in_count: 1,
    }
}

这其中有一些重复:结构体字段和函数参数名字完全相同时,写起来显得冗余。Rust 提供了字段初始化简写语法(field init shorthand) 来简化这种情况:

rust 复制代码
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username,
        email,
        sign_in_count: 1,
    }
}

当字段名和变量名相同时,可以直接省略 username: username,只写 username

4. 从已有实例更新字段:结构体更新语法

有时我们想根据现有的结构体实例创建一个新实例,只修改其中一些字段,而其余字段沿用旧实例的值。使用"结构体更新语法"(struct update syntax)能让我们少写大量重复代码。先看常规写法:

rust 复制代码
// 常规写法:手动为所有字段赋值
let user2 = User {
    active: user1.active,
    username: String::from("new_user"),
    email: String::from("another@example.com"),
    sign_in_count: user1.sign_in_count,
};

使用更新语法,只需要在花括号最后使用 .. 加上已存在的实例名称:

rust 复制代码
let user2 = User {
    email: String::from("another@example.com"),
    // 其他未指定的字段全部拷贝自 user1
    ..user1
};

这样就将 activeusernamesign_in_count 字段从 user1 中拷贝了过来,而 email 则使用了新的值。

需要注意的是,这种用法会发生所有权转移move )。如果被转移的字段(如 String)在新实例 user2 中使用了 user1 的原始值,那么 user1 中对应的字段不再有效。如果被转移的字段类型实现了 Copy trait(如 u64),则不会失效,因为它是按值拷贝的。

5. 元组结构体(Tuple Struct)

Rust 还支持一种"元组结构体"(tuple struct),其形式与常规结构体类似,只是不为字段命名,而是直接在结构体名后面写上字段类型,如同元组一样。例如:

rust 复制代码
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

    println!("Color: {}, {}, {}", black.0, black.1, black.2);
    println!("Point: {}, {}, {}", origin.0, origin.1, origin.2);
}
  • blackorigin 虽然都含有三个 i32,但它们属于不同的类型:ColorPoint
  • 这种设计可以让你在概念上区分"颜色"和"坐标点",即使它们的内部结构相同。

6. 类单元结构体(Unit-Like Struct)

Rust 允许定义没有任何字段的结构体,被称为类单元结构体(unit-like struct) 。它的用法和 ()(unit 类型)非常类似。比如:

rust 复制代码
struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

这里的 AlwaysEqual 并没有任何字段,但是将来可以为它实现某些特定的 trait,使得其任意两个实例都"总是相等"(或者做其他自定义的行为)。这种结构体常用于在类型系统层面上代表某种独特概念,或者在泛型、trait 中提供占位符作用。

7. 结构体中的所有权问题

在上面的 User 例子里,我们使用了 String 而不是 &str 作为字段类型。这是因为我们希望这个结构体拥有 自己的数据,并且在结构体生命周期内字段数据一直有效。如果结构体内部要存储引用(如 &str),则需要用到生命周期(lifetime) 来保证被引用的数据在结构体作用范围内不被释放。

如果你试图直接在结构体中写:

rust 复制代码
struct User {
    active: bool,
    username: &str, // 会出错
    email: &str,    // 会出错
    sign_in_count: u64,
}

编译器会提示需要生命周期标注(lifetime specifier),否则无法保证引用的有效性。关于如何在结构体中使用引用,以及如何正确指定生命周期,我们会在更高级的章节(通常是第 10 章)做更深入的讨论。

相关推荐
brzhang9 分钟前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构
西洼工作室17 分钟前
高效管理搜索历史:Vue持久化实践
前端·javascript·vue.js
广州华水科技24 分钟前
北斗形变监测传感器在水库安全中的应用及技术优势分析
前端
兮山与1 小时前
算法24.0
算法
开发者如是说1 小时前
Compose 开发桌面程序的一些问题
前端·架构
晓北斗NorSnow1 小时前
机器学习核心算法与学习资源解析
学习·算法·机器学习
旺代1 小时前
Token 存储与安全防护
前端
洋不写bug2 小时前
html实现简历信息填写界面
前端·html
hans汉斯2 小时前
【计算机科学与应用】基于BERT与DeepSeek大模型的智能舆论监控系统设计
大数据·人工智能·深度学习·算法·自然语言处理·bert·去噪
三十_A2 小时前
【无标题】
前端·后端·node.js