结构体

三种类型的结构体

  1. 🌟 对于结构体,我们必须为其中的每一个字段都指定具体的值
2. // fix the error
3. struct Person {
4. name: String,
5. age: u8,
6. hobby: String
7. }
8. fn main() {
9. let age = 30;
10. let p = Person {
11. name: String::from("sunface"),
12. age,
13. };
14. }
  1. 🌟 单元结构体没有任何字段。
2. struct Unit;
3. trait SomeTrait {
4. // ...定义一些行为
5. }

7. // 我们并不关心结构体中有什么数据( 字段 ),但我们关心它的行为。
8. // 因此这里我们使用没有任何字段的单元结构体,然后为它实现一些行为
9. impl SomeTrait for Unit {  }
10. fn main() {
11. let u = Unit;
12. do_something_with_unit(u);
13. }

15. // 填空,让代码工作
16. fn do_something_with_unit(u: __) {   }
  1. 🌟🌟🌟 元组结构体看起来跟元组很像,但是它拥有一个结构体的名称,该名称可以赋予它一定的意义。由于它并不关心内部数据到底是什么名称,因此此时元组结构体就非常适合。
2. // 填空并修复错误
3. struct Color(i32, i32, i32);
4. struct Point(i32, i32, i32);
5. fn main() {
6. let v = Point(__, __, __);
7. check_color(v);
8. }

10. fn check_color(p: Color) {
11. let (x, _, _) = p;
12. assert_eq!(x, 0);
13. assert_eq!(p.1, 127);
14. assert_eq!(__, 255);
15. }

结构体上的一些操作

  1. 🌟 你可以在实例化一个结构体时将它整体标记为可变的,但是 Rust 不允许我们将结构体的某个字段专门指定为可变的.
2. // 填空并修复错误,不要增加或移除代码行
3. struct Person {
4. name: String,
5. age: u8,
6. }
7. fn main() {
8. let age = 18;
9. let p = Person {
10. name: String::from("sunface"),
11. age,
12. };

14. // how can you believe sunface is only 18?
15. p.age = 30;

17. // 填空
18. __ = String::from("sunfei");
19. }
  1. 🌟 使用结构体字段初始化缩略语法可以减少一些重复代码
2. // 填空
3. struct Person {
4. name: String,
5. age: u8,
6. }
7. fn main() {}

9. fn build_person(name: String, age: u8) -> Person {
10. Person {
11. age,
12. __
13. }
14. }
  1. 🌟 你可以使用结构体更新语法基于一个结构体实例来构造另一个
2. // 填空,让代码工作
3. struct User {
4. active: bool,
5. username: String,
6. email: String,
7. sign_in_count: u64,
8. }
9. fn main() {
10. let u1 = User {
11. email: String::from("[email protected]"),
12. username: String::from("sunface"),
13. active: true,
14. sign_in_count: 1,
15. };

17. let u2 = set_email(u1);
18. }

20. fn set_email(u: User) -> User {
21. User {
22. email: String::from("[email protected]"),
23. __
24. }
25. }

打印结构体

  1. 🌟🌟 我们可以使用 #[derive(Debug)] 让结构体变成可打印的.
2. // 填空,让代码工作
3. #[__]
4. struct Rectangle {
5. width: u32,
6. height: u32,
7. }

9. fn main() {
10. let scale = 2;
11. let rect1 = Rectangle {
12. width: dbg!(30 * scale), // 打印 debug 信息到标准错误输出 stderr,并将 `30 * scale` 的值赋给 `width`
13. height: 50,
14. };

16. dbg!(&rect1); // 打印 debug 信息到标准错误输出 stderr

18. println!(__, rect1); // 打印 debug 信息到标准输出 stdout
19. }

结构体的所有权

当解构一个变量时,可以同时使用 move 和引用模式绑定的方式。当这么做时,部分 move 就会发生:变量中一部分的所有权被转移给其它变量,而另一部分我们获取了它的引用。

在这种情况下,原变量将无法再被使用,但是它没有转移所有权的那一部分依然可以使用,也就是之前被引用的那部分。

示例

2. fn main() {
3. #[derive(Debug)]
4. struct Person {
5. name: String,
6. age: Box<u8>,
7. }

9. let person = Person {
10. name: String::from("Alice"),
11. age: Box::new(20),
12. };

14. // 通过这种解构式模式匹配,person.name 的所有权被转移给新的变量 `name`
15. // 但是,这里 `age` 变量却是对 person.age 的引用, 这里 ref 的使用相当于: let age = &person.age
16. let Person { name, ref age } = person;

18. println!("The person's age is {}", age);

20. println!("The person's name is {}", name);

22. // Error! 原因是 person 的一部分已经被转移了所有权,因此我们无法再使用它
23. //println!("The person struct is {:?}", person);

25. // 虽然 `person` 作为一个整体无法再被使用,但是 `person.age` 依然可以使用
26. println!("The person's age from person struct is {}", person.age);
27. }

练习

  1. 🌟🌟
2. // 修复错误
3. #[derive(Debug)]
4. struct File {
5. name: String,
6. data: String,
7. }
8. fn main() {
9. let f = File {
10. name: String::from("readme.md"),
11. data: "Rust By Practice".to_string()
12. };

14. let _name = f.name;

16. // 只能修改这一行
17. println!("{}, {}, {:?}",f.name, f.data, f);
18. }

你可以在这里找到答案(在 solutions 路径下)