在有类的编程语言中,组织数据的方式可以定义一个类,而在 Rust 中,组织数据的方式,使用 struct。struct 可以定义三种不同的类型,一种是带有字段名字的结构体,一种是没有字段名字的元组结构体,一种是没有任何字段的单元结构体。

定义 struct

带有字段名字的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#[derive(Debug)]
struct User {
username: String,
email: String,
active: bool,
}

fn main() {
let user1 = User {
username: String::from("fred"),
email: String::from("fred@outlook.com"),
active: false,
};

println!("{:?}", user1);
}

注意上面的代码中 #[derive(Debug)],简单理解就是加上这个,可以给我们自定义的结构体添加上可打印的功能,后面就可以使用 println! 来打印出结构体。

上面的代码中定义了一个 User 结构体,里面的每一个字段,前面是字段名,后面是字段的数据类型。在 main 函数中的代码,是创建一个 User 实例。

元组结构体

1
2
3
4
5
6
7
#[derive(Debug)]
struct Color(u8, u8, u8, u8);

fn main() {
let color = Color(255,255,255,255);
println!("{:?}", color);
}

上面的代码中,定义了一个元组结构体,用来存储颜色值。

单元结构体

1
2
3
4
5
6
7
#[derive(Debug)]
struct Unit;

fn main() {
let unit = Unit;
println!("{:?}", unit);
}

通过函数创建结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#[derive(Debug)]
struct User {
username: String,
email: String,
active: bool,
}

fn create_new_user(username: String, email: String) -> User {
User{
username,
email,
active: false,
}
}

fn main() {
let new_user = create_new_user(String::from("Jack"), String::from("jack@outlook.com"));
println!("{:?}", new_user);
}

上面的代码中,create_new_user 的形参名和结构体里字段名是一样的,所以可以直接简写,而不用写成 username: username 这样。

使用其他结构体数据填充新的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#[derive(Debug)]
struct User {
username: String,
email: String,
active: bool,
}

fn main() {
let username = String::from("fred");
let email = String::from("fred@outlook.com");
let user1 = User { username, email, active: false };
println!("user1: {:?}", user1);

let username = String::from("jack");
let email = String::from("jack@outlook.com");
let user2 = User{username, ..user1};
println!("user2: {:?}", user2);
}

注意上面代码中,定义 user2 时,最后没有赋值 email 和 active 字段,而是使用了 ..user1 这样的代码,意思是说,除了手动赋值的字段,其他字段从 user1 中拷贝。

将 struct 作为函数参数

将结构体作为函数参数,通常传的是引用,否则结构体的所有权会被移动到函数里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

fn main() {
let r1 = Rectangle { width: 10, height: 10 };
let area = get_area(&r1);
println!("area: {}", area);
}

fn get_area(r: &Rectangle) -> u32 {
r.width * r.height
}

为 struct 定义方法

在 Rust 中一切皆类型,可以为任何类型实现任何方法。相当于赋予一个东西,一种功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn get_area(&self) -> u32 {
self.width * self.height
}

fn square(size: u32) -> Rectangle {
Rectangle {width: size, height: size }
}
}

fn main() {
let r1 = Rectangle { width: 10, height: 10 };
let area = r1.get_area();
println!("area: {}", area);

let square = Rectangle::square(20);
println!("square: {:?}", square);
}

为一个结构体实现的方法,需要放在 impl 结构体名 { } 内。方法形参中的 &self 是对当前实例的一个引用。而在 impl 块中,不以 self 做为参数的函数,称为关联函数,它们是与结构体相关联,而不是针对于某一个结构体实例,所以调用的时候,需要使用 结构体名::函数名 这样的形式来调用,就像 String::from("hello")

修改结构体的字段值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn get_area(&self) -> u32 {
self.width * self.height
}

fn scale(&mut self, value: u32) {
self.width *= value;
self.height *= value;
}

fn square(size: u32) -> Rectangle {
Rectangle {width: size, height: size }
}
}

fn main() {
let mut r1 = Rectangle {width: 10, height: 10};
println!("r1: {:?}", r1);
r1.scale(2);
println!("r1: {:?}", r1);
}

要修改结构体,首先要定义成 mut 的,然后结构体的方法,也要是 &mut self 的。