ex9: struct
结构体(structure)
结构体(structure,简称为 struct)是 Rust 中最为重要也是最为灵活的一种自定义数据类型。Rust 的结构体与很多语言的结构体或是类的概念并不一致,这使得这个概念更加难以理解,不妨直接从代码开始看起会比较好。
代码
代码说明
#1
Rust 提供三种不同的构造结构体的方法:
braced struct: 和 C 语言相似,使用大括号创建结构体的声明,不过没有分号进行结尾(因为并没有进行实例化)
元组结构体(tuple struct):从各个角度上都像是元组(tuple)。可以认为是带有名字的元组。在声明的时候需要使用分号结尾
单元结构体(unit struct):和单元类型(unit type)有些相似,不占用空间,可以认为只有名字。在“泛型”中很有用。
要生成一个结构体需要进行两个步骤:“声明”(declare)结构体,然后将其“实例化”(instantiate)。当你“声明”结构体时,你告诉了编译器这个结构体中有哪些内容,具有哪些属性,有哪些函数,形成一个抽象的概念。当你“实例化”结构体的时候,结构体中的数据部分被绑定了特定的具体数据,在内存中真正占据了空间,此时结构体的生命周期正式开始。
#2
在代码中有一个很奇怪的'a
标志,这个标志称为“生命周期”注释。顾名思义,“生命周期”代表了一个变量“能活多久”。
“生命周期”和“所有权”机制保证了 Rust 代码中的引用安全。我们会在一个单独的章节来说明这件事。现在只需要知道如果要在结构体中存放引用,那么标注生命周期是必要的——你可以尝试删除'a
的标注,查看编译器的报错。
#3
对于 struct 可以绑定自己的函数这件事情如果用 C 的角度来想可能有一点奇怪,不过如果从类的角度上来考虑 struct 未来就会感觉有一点奇怪。不妨从这个角度上来考虑:
struct 是一个储存数据的容器,就比如说一个饭盒;然后你可以在饭盒外面绑定一些工具,比如说筷子、勺子什么的。Rust 的结构体就是这样一种可以绑定函数的储存数据的结构,和其他编程语言的类的概念有一些近似,不过也有一点不同。如果真的要类比的话,Rust 的struct 可能更加像一个严格的 javascript 的对象(Object)类型。
你现在完全不需要会写 struct 的“成员函数”,你只要明白有这样一种形式就可以了。
#4
结构体的实例方法根据结构体的类型不同,会有不同的感受。比如说一般结构体的实例化就非常像 javascript 的对象创建,元组结构体的实例化就非常像元组,单元结构体的实例化就是直接赋值。
结构体的域的访问也和它本身的类型有关系,比如元组结构体就需要使用元组访问域的方法访问其中的元素,一般结构体的元素访问利用域名和成员描述符.
。这些操作都是比较符合直觉的。
#5
我们在 ex11 中会详细说明与打印相关的更加高级的东西,在这里我们稍微提一下。
println!("{:?}", something)
可以使用 Debug 的方式打印一个类型。所有的标准库的类型都默认继承了 Debug trait,也就是它们都可以进行 Debug 打印,比如说 Range, Vector, String(我们未来会看到)。而 struct 没有默认过这个 trait ,所以一般情况下不能直接使用"{:?}"
进行打印。
如果你希望简单地调试 struct 中的内容,可以如同代码中写的一样在 struct 声明的上一行写上#[derive(Debug)]
,这样就可以利用{:?}
进行输出。不过这样的输出也许不是你想要的,看起来可能也不是很好看。如果你需要自定义一个结构体的输出方式的话可以参看 ex11 的内容。
本节总结
在这一节中,我们针对自定义数据类型struct
进行了初步介绍。可以发现即使是简单的初步介绍我们要需要介绍很多内容,有一些内容可能还是现在没办法完全解释清楚的。你可以先掌握其中的核心概念,让自己可以看懂之后的大部分代码,至于更加深入的理解可以在之后参考更加专业的资料进行学习。
你应该对以下内容有所掌握:
三种不同的
struct
的声明以及实例化方法tuple struct 可以使用类似元组的方法进行各种操作
你应该对以下内容有所了解:
struct
可以使用impl
块定义自身的函数,并且利用实例化后的对象进行调用struct
可以通过继承Debug
trait 进行 Debug 打印如果要在
struct
中加入引用,则需要标注“生命周期”
参考资料
[2] Rust 生命周期 | 菜鸟教程
最后更新于