目录
- 一、普通结构体(struct)
- 1.1 说明
- 1.2 举例
- 1)结构体定义及访问
- 2)结构体初化的简单写法
- 3)结构体更新语法
- 二、元组结构体(tuple struct)
- 2.1 概念
- 2.2 示例
- 三、类单元结构体(unit-like structs)
- 3.1 概念
- 3.2 举例
- 四、类的使用示例
- 五、为结构体添加实现方法
- 5.1 概念
- 5.2 举例
- 六、关联函数
- 6.1 概念
- 6.2 示例
一、普通结构体(struct)
1.1 说明
- 需要为
所有字段
定义名称及类型 - 结构体可以作为函数的返回值
- 一旦struct的实例是可变的,那么实例中所有字段都是可变的
- 实例化时,结构体字段的顺序不需要和它们在声明的的顺序一致
1.2 举例
1)结构体定义及访问
//定义结构体
struct User {
username: String,
email: String,
sing_in_count: u64,
active: bool,
}
//得到一个结构体实例(未按照定义时的顺序)
fn get_new_user() -> User {
User {
email: String::from("ch@163.com"),
sing_in_count: 86,
username: String::from("ch@163.com"),
active: false,
}
}
fn main() {
//定义一个可变的结构体实例
let mut user1 = User {
email: String::from("xxx@163.com"),
username: String::from("xxx"),
active: true,
sing_in_count: 556,
};
let user2 = get_new_user();
println!("user1: email = {}", user1.email); //xxx@163.com
user1.email = String::from("yyy@189.com"); //修改可变的值
println!("user1: email = {}", user1.email); //yyy@189.com
println!("user2: email = {}", user2.email); //ch@163.com
}
2)结构体初化的简单写法
- 前面得到实例时都要重复写字段名称和变量,过于繁琐;
- 当字段名与字段值对应变量名相同时,就可以使用字段初始化简写的方式;
fn get_new_user(email: String, username: String) -> User {
User {
email,
sing_in_count: 86,
username,
active: false,
}
}
3)结构体更新语法
- 通过改变旧实例中少部分值来创建一个新的结构体实例可以使用
结构体更新语法
; .. 旧实例
语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值;
*..旧实例
必须放在最后,以指定其余的字段应从旧实例的相应字段中获取其值,但我们可以选择以任何顺序为任意字段指定值,而不用考虑结构体定义中字段的顺序;..旧实例
后不能加逗号;- 要注意
所有权转移
的问题
struct User {
username: String,
email: String,
sing_in_count: u64,
active: bool,
}
fn get_new_user() -> User {
let username = String::from("ch@163.com");
let email = String::from("ch@163.com");
User {
username,
email,
sing_in_count: 86,
active: false,
}
}
fn main() {
let user1 = get_new_user();
//未采用更新语法
let user2 = User{
username: String::from("user2"),
email: String::from("user2@163.com"),
sing_in_count: user1.sing_in_count,
active: user1.active,
};
//采用结构体更新语法
let user3 = User{
username: String::from("user3"),
..user1 //不能写逗号,其它的用user1的赋值
};
// println!("user1.username = {}", user1.email); //所有权已经转移到user3,所以这里不能使用
println!("user2.username = {}, sing_in_count = {}", user2.username, user2.sing_in_count);
println!("user3.username = {}, sing_in_count = {}", user3.username, user3.sing_in_count);
}
二、元组结构体(tuple struct)
2.1 概念
- 定义与元组类似的结构体,称为
元组结构体
; - 元组结构体有着结构体名称提供的含义,但
没有具体的字段名,只有字段的类型
; - 当想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,可以使用元组结构体;
- 要定义元组结构体,以 struct 关键字和结构体名开头并后跟元组中的类型;
2.2 示例
fn main() {
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(3, 4, 5);
let origin = Point(33, 15, 14);
println!("{} {} {}", black.0, black.1, black.2);
println!("{} {} {}", origin.0, origin.1, origin.2);
}
三、类单元结构体(unit-like structs)
3.1 概念
- 类单元结构体是一个
没有任何字段的结构体
; - 类似于 (),即单元类型(只有一个值,被称为单元值);
- 类单元结构体常常在想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用;
3.2 举例
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
- 使用struct关键字定义 AlwaysEqual,以分号结尾;
- 在main函数中,以代码所示的方法获得 AlwaysEqual 的实例变量 subject;
- 下一步将实现这个类型的行为,即每个实例始终等于每一个其他类型的实例,也许是为了获得一个已知的结果以便进行测试。现在不需要任何数据来实现这种行为。在以后的文章里将会看到如何定义特性并在任何类型上实现它们,包括类单元结构体。
四、类的使用示例
#[derive(Debug)]
struct Rectangle{
width: u32,
height: u32,
}
//只传递引用,因此不会获取传入变量的所有权
fn area(rect: &Rectangle) -> u32 {
rect.width * rect.height
}
fn main() {
let rc = Rectangle{
width: 53,
height: 20,
};
println!("area = {}", area(&rc));
println!("rc = {:#?}", rc)
}
- 代码计算长方体的面积;
area
传入的是引用,因此不会获得变量的所有权;#[derive(Debug)]
与println的{:#?}
配合使用是为了查看实体化后rc的值,如果把前者取消并且使用println!("rc={}",rc)
打印结果,会得到如下错误
按照说明修改println!
的参数再编译会得到如下错误
因此,根据提示一步步修改就会得到正确的结果
五、为结构体添加实现方法
5.1 概念
- 使用
fn
关键字和名称声明,可以拥有参数和返回值; - 方法在结构体的
上下文中被定义(或者是枚举或 trait 对象的上下文)
,并且第一个参数总是self
,代表调用该方法的结构体实例; - 一般将方法的第一个参数写为
&self
,只希望能够读取结构体中的数据,而不是写入; - 想要在方法中改变调用方法的实例,需要将第一个参数改为
&mut self
; - 通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少用的;该功能通常用在当方法将 self 转换成别的实例的时候,想要防止调用者在转换之后使用原始的实例。
5.2 举例
area
方法实现了面积的计算;can_hold
方法计算当前对象是否能够包围住第二个Rectangle对象的实例;
#[derive(Debug)]
struct Rectangle{
width: u32,
height: u32,
}
impl Rectangle{
fn area(&self) -> u32{
self.width * self.height
}
fn can_hold(&self, src: &Rectangle) -> bool{
self.width >= src.width && self.height >= src.height
}
}
fn main() {
let rc = Rectangle{
width: 53,
height: 20,
};
let rc2 = Rectangle{
width: 51,
height: 20,
};
println!("{}", rc.can_held(&rc2));
}
六、关联函数
6.1 概念
- 所有在 impl 块中定义的函数被称为关联函数;
- 可以定义
不以 self
为第一参数的关联函数(类似于类的静态函数),String::from
就是一个关联函数;
6.2 示例
使用关联函数创建正文形并计算面积。
#[derive(Debug)]
struct Rectangle{
width: u32,
height: u32,
}
impl Rectangle{
fn area(&self) -> u32{
self.width * self.height
}
fn square(size: u32) -> Rectangle{
Rectangle{
width: size,
height: size,
}
}
}
fn main() {
let rc = Rectangle::square(3);
println!("rc.area = {}", rc.area());
}