定义和实例化结构
结构体类似于 Tuples ,在 “Tuples Type” 一节中讨论,因为两者都包含多个相关值。与元组一样, 结构体的片段可以是不同的类型。与元组不同,在 struct 您将为每条数据命名,以便清楚地了解值的含义。添加这些 names 意味着结构体比 Tuples 更灵活:您不必依赖 按数据的顺序指定或访问实例的值。
要定义结构体,我们输入关键字struct
并命名整个结构。一个
struct 的名称应该描述数据片段的重要性
组合在一起。然后,在大括号内,我们定义
数据片段,我们称之为 fields。例如,示例 5-1 显示了一个
结构体,用于存储有关用户帐户的信息。
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() {}
User
结构体定义要在定义结构后使用它,我们创建该结构的实例 通过为每个字段指定具体值。我们通过以下方式创建实例 说明结构的名称,然后添加包含 key 的大括号: value 对,其中 keys 是字段的名称,values 是 数据。我们不必在 我们在 struct 中声明它们的顺序相同。换句话说, struct definition 就像该类型的通用模板,实例填充 在该模板中,创建该类型的值。为 例如,我们可以声明一个特定的用户,如图 5-2 所示。
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { let user1 = User { active: true, username: String::from("someusername123"), email: String::from("someone@example.com"), sign_in_count: 1, }; }
User
结构要从结构体中获取特定值,我们使用点表示法。例如,到
访问此用户的电子邮件地址,我们使用user1.email
.如果实例为
可变的,我们可以通过使用点表示法并赋值为
特定字段。示例 5-3 显示了如何更改email
变量的 fieldUser
实例。
struct User { active: bool, username: String, email: String, sign_in_count: u64, } 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"); }
email
字段User
实例请注意,整个实例必须是可变的;Rust 不允许我们标记 只有某些字段是可变的。与任何表达式一样,我们可以构造一个新的 实例作为函数体中的最后一个表达式,以 隐式返回该新实例。
示例 5-4 显示了一个build_user
函数返回一个User
实例替换为
给定的电子邮件和用户名。这active
field 获取true
和
这sign_in_count
获取1
.
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn build_user(email: String, username: String) -> User { User { active: true, username: username, email: email, sign_in_count: 1, } } fn main() { let user1 = build_user( String::from("someone@example.com"), String::from("someusername123"), ); }
build_user
函数接受 email 和 username 并返回一个User
实例使用与 struct 相同的名称命名函数参数是有意义的
字段,但必须重复email
和username
字段名称和
variables 有点乏味。如果结构体具有更多字段,则重复每个名称
会变得更烦人。幸运的是,有一个方便的简写!
使用 Field init 简写
因为 parameter name 和 struct field name 在
示例 5-4,我们可以使用 field init 速记语法来重写build_user
因此,它的行为完全相同,但没有username
和email
,如示例 5-5 所示。
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn build_user(email: String, username: String) -> User { User { active: true, username, email, sign_in_count: 1, } } fn main() { let user1 = build_user( String::from("someone@example.com"), String::from("someusername123"), ); }
build_user
函数使用字段初始化简写,因为username
和email
参数与 struct 字段同名在这里,我们将创建一个User
struct 中,它有一个字段
叫email
.我们想要设置email
字段的值设置为email
参数的build_user
功能。因为email
field 和
这email
parameter 的 name 都一样,我们只需要写email
而
比email: email
.
使用 Struct Update 语法从其他实例创建实例
创建包含大部分 的值,但会更改一些值。您可以使用 struct update 语法来执行此作。
首先,在示例 5-6 中,我们展示了如何创建一个新的User
实例user2
,没有 UPDATE 语法。我们为email
但
否则,请使用user1
我们在示例 5-2 中创建的。
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { // --snip-- let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; let user2 = User { active: user1.active, username: user1.username, email: String::from("another@example.com"), sign_in_count: user1.sign_in_count, }; }
User
实例使用除user1
使用 struct update 语法,我们可以用更少的代码实现相同的效果,因为
如示例 5-7 所示。语法..
指定其余字段不
explicitly set 的值应与给定实例中的字段相同。
struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn main() { // --snip-- let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; let user2 = User { email: String::from("another@example.com"), ..user1 }; }
email
值User
实例,但要使用user1
示例 5-7 中的代码还在user2
具有
不同的值email
但对username
,active
和sign_in_count
字段user1
.这..user1
必须排在最后
要指定任何剩余字段都应从
中的相应字段user1
,但我们可以选择为
许多字段,无论
结构的定义。
请注意,struct update 语法的使用类似于赋值;这是因为
它移动数据,就像我们在“变量和数据交互
移动“部分。在这个例子中,我们不能再使用=
user1
作为一个整体创建user2
因为String
在username
字段为user1
已移至user2
.如果我们给予user2
新增功能String
值email
和username
,因此只使用了active
和sign_in_count
值来自user1
然后user1
仍将是
创建后有效user2
.双active
和sign_in_count
是
实现Copy
trait 中,因此我们在“Stack-Only
Data: Copy“部分将适用。我们仍然可以使用user1.email
在此示例中,因为它的值未移出。
使用不带命名字段的 Tuple 结构创建不同的类型
Rust 还支持看起来类似于元组的结构,称为元组结构。 元组结构具有结构名称提供的附加含义,但没有 与其字段关联的名称;相反,它们只有 领域。当你想给整个元组起个名字时,元组结构很有用 并使元组成为与其他元组不同的类型,并在将每个 field 将是 verbose 或 redundant。
要定义元组结构体,请从struct
keyword 和结构名称
后跟元组中的类型。例如,这里我们定义并使用两个
名为Color
和Point
:
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 中的字段可能具有相同的类型。为
示例,一个函数采用Color
不能拿Point
作为参数,即使这两种类型都由三个i32
值。否则,元组结构实例类似于元组,因为你可以
将它们分解为单独的部分,您可以使用.
跟着
通过索引访问单个值。
没有任何字段的类似 Unit 的结构体
您还可以定义没有任何字段的结构体!这些被称为单元状结构,因为它们的行为类似于 ,即
我们在 “元组类型” 一节中提到过。类单元
当你需要在某种类型上实现 trait 但不需要时,结构体可能很有用
具有要存储在类型本身中的任何数据。我们将讨论特征
在第 10 章中。下面是一个声明和实例化 unit 结构体的示例
叫()
AlwaysEqual
:
struct AlwaysEqual; fn main() { let subject = AlwaysEqual; }
定义AlwaysEqual
,我们使用struct
keyword,即我们想要的名称,以及
然后是分号。无需大括号或圆括号!然后我们可以得到一个
实例AlwaysEqual
在subject
变量:使用
name 定义的,没有任何大括号或圆括号。想象一下以后
我们将为此类型实现行为,以便每个AlwaysEqual
总是等于任何其他类型的每个实例,也许是
具有用于测试目的的已知结果。我们不需要任何数据来
实现该行为!您将在第 10 章中看到如何定义 trait 和
在任何类型的结构体上实现它们,包括类似单元的结构体。
结构体数据的所有权
在User
struct 定义中,我们使用了String
type 而不是&str
string slice 类型。这是一个经过深思熟虑的选择
因为我们希望这个结构体的每个实例都拥有它的所有数据,并且对于
只要整个结构有效,该数据就有效。
结构也可以存储对某物所拥有的数据的引用 else,但要做到这一点,需要使用 lifetimes,我们将 在第 10 章中讨论。生命周期确保结构体引用的数据 只要结构有效,就有效。假设你尝试存储一个引用 在未指定生命周期的结构体中,如下所示;这不起作用:
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 run
Compiling structs v0.1.0 (file:///projects/structs)
error[E0106]: missing lifetime specifier
--> src/main.rs:3:15
|
3 | username: &str,
| ^ expected named lifetime parameter
|
help: 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 parameter
|
help: 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 2 previous errors
在第 10 章中,我们将讨论如何修复这些错误,以便您可以存储
引用,但现在,我们将使用 owned 修复此类错误
类型,如String
而不是像&str
.
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准