写点什么

Rust 从 0 到 1- 结构体 - 定义和实例化

用户头像
关注
发布于: 2021 年 04 月 07 日
Rust从0到1-结构体-定义和实例化

struct,或者 structure,是一个自定义数据类型,允许你命名并且包装多个相关的值,从而形成一个有意义的组合。struct 就像 Java 对象中的类属性。后面,将会对比元组与结构体的异同,演示结构体的用法,并讨论如何在结构体上定义方法和关联函数来指定与结构体数据相关的行为。你可以在程序中基于结构体和枚举(enum,后面章节介绍)创建新类型,以充分利用 Rust 的编译时类型检查。

和元组一样,结构体的每一部分可以是不同类型。但不同于元组,结构体需要命名各部分数据,借此可以清楚的表明其值的意义,即,字段(field)。由于有了字段,结构体比元组更灵活,不需要依赖顺序来访问实例中的值。参考下面的例子:

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

一旦定义了结构体后,我们可以通过为每个字段指定具体值来创建这个结构体的实例,用 key: value 键-值对的形式提供字段,其中 key 是字段的名字,value 是需要存储在字段中的数据值,并且字段的顺序不需要和它们在结构体中声明的顺序一致(和 JSON 挺像的)。参考下面的例子:

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

为了从结构体中获取某个字段的值,可以使用 . 号。如果我们只想要用户的邮箱地址,可以用 user1.email。要更改结构体中的值,需要结构体的实例定义是可变的,然后可以使用点号为指定的字段赋值。参考下面的例子:

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

另外,需要注意的是 Rust 不允许只将某个字段标记为可变,只能将整个结构体类型的变量定义为可变。而且同其他类型一样,我们可以在函数体的最后一个表达式中构造一个结构体的实例,来隐式地返回这个实例:

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

在上面的例子中,实例化结构体时重复书写 email 和 username 其实是有点烦人的,特别是当结构体有更多字段,重复每个名称就更加烦人了。Rust 在这里提供了一个语法糖,有一个方便的简写方法。

结构体实例化的简写语法

当函数参数名与字段名完全相同时,可以直接使用看函数参数,参考下面的例子:

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

由于函数参数 email 和 username 和结构体 User 的字段名完全相同,因此可以直接使用,这样就方便多了,也可以帮我们养成统一命名的好习惯;)。

使用其他实例字段实例化结构体

在和其他实例的大部分值相同但有部分值不同的场景下,可以通过结构体更新语法(struct update syntax)实现:

let user2 = User {    email: String::from("another@example.com"),    username: String::from("anotherusername567"),    ..user1};
复制代码

.. 语法代表剩余未显式设置值的字段与给定实例对应字段相同的值。需要注意的是,必须是相同结构体类型的实例。

没有命名字段的结构体

也可以定义没有字段名的结构体,称为元组结构体(tuple structs)。元组结构体可以根据结构体名称区分不同类型,但没有具体的字段名,只有字段的类型。或者说你想给整个元组取一个名字并用于区分不同类型,像普通结构体那样为每个字段命名就显得比较麻烦(想的真是周到啊):

struct Color(i32, i32, i32);struct Point(i32, i32, i32);
let black = Color(0, 0, 0);let origin = Point(0, 0, 0);
复制代码

black 和 origin 的值和类型虽然相同,但是由于它们是不同的元组结构体的实例,他们属于不同的类型。因此,一个使用 Color 类型参数的函数不能接受 Point 作为参数。在其他方面,元组结构体实例和元组类似:可以将其解构为单独的部分,也可以使用 . 后跟索引来访问具体的单个值等。

没有任何字段的结构体

没有任何字段的结构体被称为单元结构体(unit-like structs),因为它们类似于 (),即 unit 类型(和 void 类似,就是没有任何返回值)。这种结构体常常用在在某个类型上实现 trait,但不需要在类型中存储数据的场景下。trait 将在后续章节介绍。

结构体数据的所有权

上面的例子中 User 结构体使用了自身拥有所有权的 String 类型而不是 &str 字符串切片类型。这时候只要整个结构体是有效的话其数据也是有效的。也可以使结构体存储数据的引用,不过这么做的话需要用上生命周期(lifetimes), 这个功能会在后面介绍。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中使用引用而不指定生命周期将会报错:

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

后面会讲到如何修复这个问题以便在结构体中使用引用。

发布于: 2021 年 04 月 07 日阅读数: 14
用户头像

关注

公众号"山 顽石"欢迎大家关注;) 2021.03.23 加入

IT老兵,关注Rust、Java、前端、架构、大数据、AI、算法等;平时喜欢美食、旅游、宠物、健身、篮球、乒乓球等。希望能和大家交流分享。

评论

发布
暂无评论
Rust从0到1-结构体-定义和实例化