5.1 定义和实例化-Struct
Struct示例(与C++类似)
1 | struct User { |
在创建示例时,不需要按照声明里的顺序来初始化每个字段,例如
1 | let user1 = User { |
- 所有字段都必须被初始化!
如果要访问实例的某个值采取点标记法,例如
1 | let email = user1.email; |
如果需要改变struct中的值,需要在声明的时候保证其是mutable,例如
1 | let mut user1 = User { |
mut
关键字加在实例这里,而不是Struct声明中- 一旦该实例是可变的,所有字段都必须可变
struct可以作为函数的返回值,例如
1 | fn build_user(email: String, user: String) -> User { |
当字段名和字段对应的变量名相同时,可以适用字段初始化简写,例如
1 | User { |
当我们希望创建一个实例2,它和已有的实例1有部分内容重叠,那么可以采用struct的更新语法来快速创建
1 | let mut user2 = User { |
Tuple struct
tuple struct是另一种数据结构,兼有tuple和struct的特点:
- 整体有名字,里面元素没名字
- 适用于想给整个tuple起名字但不希望它和其它tuple一样的情况
例如
1 | struct Color(i32, i32, i32); |
- 这里
black
和origin
是不同的tuple struct的实例
Tuple struct的行为更像struct,可以使用模式匹配,也可以使用点索引
Unit-Like struct
这种struct没有任何字段,类似于单位类型的地位
适用于在某个类型上实现trait,但在里面有没有想存放的数据
Struct的所有权
每个struct实例拥有实例里所有的数据,所以只要该实例有效,里面字段也有效
- struct也可以存放引用,需要借助生命周期
- 生命周期保证只要struct的实例是有效的,里面引用也是有效的
5.2 struct示例
实现一个计算长方形面积的程序:
1 | struct Rectangle { |
如果我们想使用下面的语句
1 | println!("{}", rectangle); |
- 因为我们定义的struct还没有实现
std::fmt::Display
这个trait - 大多数基本数据结果是默认实现的,所以可以用formater来打印
一个解决方案是使用std::fmt::Debug
这个trait
1 | println!("{:?}", rectangle); |
这需要我们实现Debug方法,可以在定义struct时声明
1 |
|
- 其中
derive
是派生的意思
如果希望按列打印struct的实例,可以适用
1 | println!("{:#?}", rectangle); |
5.3 struct里的方法
我们可以在struct里定义方法(语法与函数定义类似),类似于面向对象里的类成员函数
- 方法是在struct(或enum或trait)的上下文里定义
- 其第一个参数为
self
例如前面计算面积的例子,我们可以采用
1 | impl Rectangle { |
这里
impl
为implementation的缩写,用于定义名为Rectangle
的struct的方法self
会被推断为struct类型本身方法里的参数可以不选择borrow,即
self
和&mut self
都是可以的
在C++中,一般使用object->func()
或者(*object).func()
来调用对象的方法(后者先解引用)
在Rust中,编译器会根据情况自动添加&
,&mut
,或*
来匹配方法的签名,例如
p1.distance(&p2)
和(&p1).distance(&p2)
是等价的
类似的,也可以定义多个参数的方法,例如
1 | fn can_hold(&self, other: &Rectangle) -> bool { |
关联函数
我们可以在impl
里面定义不以&self
作为第一个参数的函数,称为关联函数(不是方法),例如
1 | String::from() |
一般用作构造器来创建实例,例如
1 | impl Rectangle { |
- 其中的
Rectangle::square()
就是一个关联函数 - 这里的符号
::
一般出现在关联函数中,此外,它也会出现在命名空间里
此外,每个struct允许定义多个impl
快