当前位置: 首页 > news >正文

rust-数据结构

定义和实例化结构体

结构体类似于“元组类型”一节中讨论的元组,因为两者都包含多个相关的值。像元组一样,结构体中的各个部分可以是不同的类型。但与元组不同的是,在结构体中你需要为每个数据部分命名,以便明确这些值的含义。添加这些名称使得结构体比元组更灵活:你不必依赖数据的顺序来指定或访问实例中的值。

要定义一个结构体,我们输入关键字 struct 并为整个结构体命名。结构体的名称应描述被组合在一起的数据部分的重要性。然后,在花括号内定义数据部分(称为字段)的名称和类型。例如,清单 5-1 展示了一个存储用户账户信息的结构体。

文件名:src/main.rs

struct User {active: bool,username: String,email: String,sign_in_count: u64,
}

清单 5-1:User 结构体定义
在定义好一个结构体后,要使用它,我们通过为每个字段指定具体值来创建该结构体的实例。我们通过写出该结构体名称,然后加上包含键值对(key: value)的花括号来创建实例,其中键是字段名,值是我们想存储在这些字段中的数据。我们不必按照声明时的顺序指定字段。换句话说,结构体验证就像一种通用模板,而实例则用特定的数据填充这个模板,从而创建该类型的具体数值。例如,我们可以如清单 5-2 所示声明一个特定用户。
文件名: src/main.rs

fn main() {
let mut user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
}

清单 5-3:修改 User 实例中 email 字段的值
注意,整个实例必须是可变的;Rust 不允许我们只标记某些字段为可变。与任何表达式一样,我们可以在函数体的最后一个表达式中构造结构体的新实例,以隐式返回该新实例。
清单 5-4 展示了一个 build_user 函数,它返回一个带有给定 email 和 username 的 User 实例。active 字段被赋值为 true,sign_in_count 被赋值为 1。
文件名: src/main.rs

fn build_user(email: String, username: String) -> User {
User {
active: true,
username: username,
email: email,
sign_in_count: 1,
}
}

清单 5-4:一个接受 email 和 username 并返回 User 实例的 build_user 函数
将函数参数命名为与结构体字段相同的名字是合理的,但重复写出 email 和 username 字段名称及变量有点繁琐。如果结构体有更多字段,重复每个名字会更加烦人。幸运的是,有一种方便的简写方式!

使用字段初始化简写

因为参数名和结构体字段名在清单 5-4 中完全相同,我们可以使用字段初始化简写语法重写 build_user,使其行为完全相同,但不必重复书写 username 和 email,如清单 5-5 所示。

文件名:src/main.rs

fn build_user(email: String, username: String) -> User {User {active: true,username,email,sign_in_count: 1,}
}

清单 5-5:一个使用字段初始化简写的 build_user 函数,因为 username 和 email 参数与结构体字段同名

这里我们创建了一个新的 User 实例,它有一个名为 email 的字段。我们想将该 email 字段的值设置为 build_user 函数中 email 参数的值。由于 email 字段和参数名称相同,我们只需写成 email,而不是 email: email

用结构体更新语法从其他实例创建新实例

通常需要基于另一个实例的大部分值来创建新实例,但修改其中一些值。这时可以使用结构体更新语法。

首先,在清单 5-6 中展示了如何常规地(不使用更新语法)创建 user2 新的 User 实例。我们给 email 设置了新值,其余则沿用了之前在清单 5-2 创建的 user1 的对应值。

文件名:src/main.rs

fn main() {// --snip--let user2 = User {active: user1.active,username: user1.username,email: String::from("another@example.com"),sign_in_count: user1.sign_in_count,};
}

清单 5-6:用除了一个之外全部来自 user1 值的新建 User 实例

利用结构体更新语法,可以用更少代码实现相同效果,如清单 5-7 所示。.. 表示未显式设置的剩余字段应取自指定实例中的对应字段。

文件名:src/main.rs

fn main() {// --snip--let user2 = User {email: String::from("another@example.com"),..user1};
}

清单 5-7:用结构体更新语法为 User 实例设定新的邮箱,同时其它所有值都来自于 user1

如上所示,user2 拥有不同的邮箱,但用户名、active 和 sign_in_count 等其它字段均继承自 user1。..user1 必须放最后,用以指明剩余未赋值的字段从哪个实例获取。但你可按任意顺序指定任意数量的具体字段,无需遵循结构定义中各个域出现顺序。

注意,结构体更新语法看起来像赋值操作符 =;这是因为它会移动数据,就像“变量与数据交互中的移动”章节讲到的一样。在本例中,由于用户名字符串被移入到了 new 用户,所以不能再继续使用原来的 user1 。如果给 user2 提供了新的字符串类型邮箱和用户名,只复用了 activesign_in_count 两个实现 Copy trait 的类型,那么在构造完后仍然能继续有效地访问原来的 user1 。这两个类型属于栈上复制的数据,因此适用于“仅限栈上的数据拷贝”章节讨论过的方法。本例还能访问到原来用户里的邮件地址,因为它没有被移动出去。

无命名域元组结构用于区分不同类型

Rust 同样支持类似元组但带名字标识符号称作元组结构(tuple structs)。元组结构拥有额外含义,即由其名字提供,但其内部元素没有独立命名,仅包含元素类型。当你希望整体给这个元组起个名字,并使得此类元组区别于其他普通元组时非常有用,也适合当每个成员逐一定义名称显得冗长或多余时采用。

定义方式是先声明 struct,再跟上名称及括号内列出的各元素类型。例如,这里定义并且使用两个分别叫 Color 和 Point 的元组结构:

文件名:src/main.rs

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

请注意,black 与 origin 是不同类型,因为它们分别是不同元组结 构(struct) 类型下生成出来的对象。每种 struct 都是独立的新类型,即便里面成员都是一样的数据型别。例如,一个函数若要求传入 Color 类型,则不能传入 Point 类型作为实参,即使两者都由三个 i32 数字组成。此外,虽然 tuple struct 在解构拆包以及通过索引点位访问方面类似普通 tuple,不同的是解构时必须明确指出要解构的是哪种 tuple struct,比如要这样写:

let Point(x, y, z) = point;

无任何字段的类单元(struct Unit-Like Structs)

你还可以定义不包含任何字段的 struct!这些被称作类单元素(struct unit-like structs),因为它们行为类似于 () 单位值(在“Tuple Type”章节中提到过)。当你需要对某种类型实现 trait,但不需要在该类型本身存储任何数据时,类单元素很有用。我们将在第10章讨论 trait。这是声明和实例化一个名为 AlwaysEqual 的类单元素示例:

文件名:src/main.rs

struct AlwaysEqual;fn main() {let subject = AlwaysEqual;
}

定义 AlwaysEqual 时,我们使用了 struct 关键字、所需名称,然后以分号结尾。不需要花括号或圆括号!然后我们就能像这样获取 AlwaysEqual 实例:直接用已定义好的名字,不带花括号或圆括号。假设以后我们会实现这个类型的一些行为,使得所有 AlwaysEqual 实例总是等价于任意其他实例,比如用于测试目的且结果可预知。这种行为无需存储任何数据即可实现!第10章将介绍如何定义 trait 并将其应用到包括类单元素在内任意类型上。

Struct 数据所有权

在清单5-1中的 User struct 定义里,我们用了拥有所有权(owned)的 String 类型,而非字符串切片 &str。这是刻意选择,因为希望每个该 struct 实例拥有自身全部数据,并保证这些数据只要整个 struct 有效就一直有效。

当然,也可以让 structs 存储指向其它地方拥有的数据引用,但这要求使用生命周期(lifetimes),这是 Rust 的一项特性,第10章会详细讲解。生命周期确保被引用的数据只要对应 struct 有效就保持有效。如果尝试在未指定生命周期情况下,将引用存入 struct,如下代码,则无法编译:

文件名:src/main.rs

struct User {active: bool,username: &str,email: &str,sign_in_count: u64,
}fn main() {let user1 = User {active: true,username: "someusername123",email: "someone@example.com",sign_in_count: 1,};
}

编译器会报错提示缺少生命周期说明符:

$ cargo runCompiling structs v0.1.0 (file:///projects/structs)
error[E0106]: missing lifetime specifier--> src/main.rs:3:15|
3 |     username: &str,|               ^ expected named lifetime parameterhelp: consider introducing a named lifetime parameter
|
1 ~ struct User<'a> {
2 |     active: bool,
3 ~     username: &'a str,error[E0106]: missing lifetime specifier--> src/main.rs:4:12|
4 |     email: &str,|            ^ expected named lifetime parameterhelp: consider introducing a named lifetime parameter
|
1 ~ struct User<'a> {
2 |     active: bool,
3 |     username:&str,
4 ~     email:&'a str,For more information about this error, try `rustc --explain E0106`.
error : could not compile `structs` (bin "structs") due to previous errors.

第10章中我们将讲述如何修复此错误,以便能够把引用存入 structs;但目前,我们通过改用拥有所有权的数据如 String 来替代引用(&str),从而避免此类错误。

http://www.dtcms.com/a/295388.html

相关文章:

  • 聚观早报 | 猿编程推动中美青少年AI实践;华为Pura 80数字版售价公布;iPhone 17 Air电池曝光
  • Redis数据类型与内部编码
  • 国产数据库拐点已至:电科金仓用“融合+AI”重新定义下一代数据底座
  • rustfs/rustfs基于 Rust 的高性能分布式存储系统
  • 进程通信----匿名管道
  • 进阶向:基于Python的本地文件内容搜索工具
  • 加入淘宝联盟内容库,以便在B站等平台被推广
  • 我的新项目又来咯!
  • iOS 抓包工具有哪些?按能力划分的实用推荐与使用心得
  • 开发运维DevOps(附电子书资料)
  • 办公自动化入门:如何高效将图片整合为PDF文档
  • 7月25日 矩阵起源亮相深圳DA数智大会,解读多模态大模型驱动的数据处理新方法
  • 如何保证GPFS文件系统的强一致性
  • PDF转Markdown - Python 实现方案与代码
  • Go进阶高并发(多线程)处理教程
  • 中小企业安全落地:低成本漏洞管理与攻击防御方案
  • 新手操作steam搬砖项目,应该如何快速起步
  • 图机器学习(19)——金融数据分析
  • 深度分析Java类加载机制
  • 医疗AI轻量化部署方案的深度梳理与优化路径判研
  • k8s把某个secret挂在某命名空间下
  • MySQL深度理解-MySQL事务优化
  • 现代C++的一般编程规范
  • 【CMake】CMake 常用语法总结
  • SSP通过SDK对接流量的原理与实现
  • SSM之表现层数据封装-统一响应格式全局异常处理
  • 主要分布在背侧海马体(dHPC)CA1区域(dCA1)的位置细胞对NLP中的深层语义分析的积极影响和启示
  • 大模型处理私有数据的核心技术
  • 《R 矩阵》
  • 基础NLP | 02 深度学习基本原理