rust_learning/
struct_def.rs

1struct User {
2    username: String,
3    email: String,
4    sign_in_count: u64,
5    active: bool,
6}
7
8/// 结构体
9pub fn struct_def() {
10    // 实例化结构体
11    let user = User {
12        email: String::from("someone@example.com"),
13        username: String::from("someusername123"),
14        active: true,
15        sign_in_count: 1,
16    };
17
18    // 可变结构体
19    // 注意所有权的移动,所以在 user2 中,user的username 和 email 的所有权已经移动到 user2 中。
20    let mut user2 = User {
21        email: String::from("sometwo@example.com"),
22        // 结构体更新语法
23        ..user
24    };
25    user2.active = false;
26    // value borrowed here after move
27    // println!("{}", user.username);
28    let user3 = struct_build_user(String::from("some3"), String::from("some3@example.com"));
29}
30
31fn struct_build_user(username: String, email: String) -> User {
32    // 当变量的名称与结构体字段名称相同时,可以使用简写语法,也可以写完整
33    User {
34        username,
35        email,
36        active: true,
37        sign_in_count: 1,
38    }
39}
40
41#[derive(Debug)]
42struct Color(i32, i32, i32);
43#[derive(Debug)]
44struct Point(i32, i32, i32);
45
46/*
47    元组结构体(tuple structs)。
48*/
49/// 元组结构体
50pub fn tuple_struct() {
51    // 元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。
52    // 当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,
53    let black = Color(0, 0, 0);
54    println!("{:#?}", black);
55    let origin = Point(0, 0, 0);
56    println!("{:#?}", origin);
57}
58
59// struct 没有字段
60// 类单元结构体
61// 类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。
62struct AlwaysEqual;
63fn struct_no_field() {
64    let subject = AlwaysEqual;
65}
66
67
68#[derive(Debug)]
69struct Rectangle {
70    width: u32,
71    height: u32,
72}
73// 所有在 impl 块中定义的函数被称为 关联函数(associated functions),因为它们与 impl 后面命名的类型相关。
74// 不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。这些函数的名称通常为 new ,但 new 并不是一个关键字。
75impl Rectangle {
76    // 我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。
77    // 如果想要在方法中改变调用方法的实例,需要将第一个参数改为 &mut self。
78    // 通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少见的;
79    // 这种技术通常用在当方法将 self 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。
80
81    //通常,但并不总是如此,与字段同名的方法将被定义为只返回字段中的值,而不做其他事情。
82    // 这样的方法被称为 getters,Rust 并不像其他一些语言那样为结构字段自动实现它们。
83    // Getters 很有用,因为你可以把字段变成私有的,但方法是公共的,这样就可以把对字段的只读访问作为该类型公共 API 的一部分。
84
85    // Rust 有一个叫 自动引用和解引用(automatic referencing and dereferencing)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。
86    // 它是这样工作的:当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &、&mut 或 * 以便使 object 与方法签名匹配。也就是说,这些代码是等价的:
87    //
88    //
89    // p1.distance(&p2);
90    // (&p1).distance(&p2);
91
92    // 在给出接收者和方法名的前提下,Rust 可以明确地计算出方法是仅仅读取(&self),做出修改(&mut self)或者是获取所有权(self)。
93    fn area(&self) -> u32 {
94        self.width * self.height
95    }
96}
97
98impl Rectangle {
99    fn can_hold(&self, other: &Rectangle) -> bool {
100        self.width > other.width && self.height > other.height
101    }
102}
103
104/// 方法
105pub fn struct_method() {
106    let rect1 = Rectangle {
107        width: dbg!(30),
108        height: 50,
109    };
110    // 另一种使用 Debug 格式打印数值的方法是使用 dbg! 宏。
111    // dbg! 宏接收一个表达式的所有权(与 println! 宏相反,后者接收的是引用),打印出代码中调用 dbg! 宏时所在的文件和行号,
112    // 以及该表达式的结果值,并返回该值的所有权。
113    // 注意:调用 dbg! 宏会打印到标准错误控制台流(stderr),与 println! 不同,后者会打印到标准输出控制台流(stdout)。
114    dbg!(&rect1);
115    println!(
116        "The area of the rectangle is {} square pixels.",
117        rect1.area()
118    );
119
120
121    let rect2 = Rectangle {
122        width: 10,
123        height: 40,
124    };
125
126    let rect3 = Rectangle {
127        width: 60,
128        height: 45,
129    };
130
131
132    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
133}