方法和关联函数
示例
1. struct Point {
2. x: f64,
3. y: f64,
4. }
6. // `Point` 的关联函数都放在下面的 `impl` 语句块中
7. impl Point {
8. // 关联函数的使用方法跟构造器非常类似
9. fn origin() -> Point {
10. Point { x: 0.0, y: 0.0 }
11. }
13. // 另外一个关联函数,有两个参数
14. fn new(x: f64, y: f64) -> Point {
15. Point { x: x, y: y }
16. }
17. }
19. struct Rectangle {
20. p1: Point,
21. p2: Point,
22. }
24. impl Rectangle {
25. // 这是一个方法
26. // `&self` 是 `self: &Self` 的语法糖
27. // `Self` 是当前调用对象的类型,对于本例来说 `Self` = `Rectangle`
28. fn area(&self) -> f64 {
29. // 使用点操作符可以访问 `self` 中的结构体字段
30. let Point { x: x1, y: y1 } = self.p1;
31. let Point { x: x2, y: y2 } = self.p2;
33. // `abs` 是一个 `f64` 类型的方法,会返回调用者的绝对值
34. ((x1 - x2) * (y1 - y2)).abs()
35. }
37. fn perimeter(&self) -> f64 {
38. let Point { x: x1, y: y1 } = self.p1;
39. let Point { x: x2, y: y2 } = self.p2;
41. 2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
42. }
44. // 该方法要求调用者是可变的,`&mut self` 是 `self: &mut Self` 的语法糖
45. fn translate(&mut self, x: f64, y: f64) {
46. self.p1.x += x;
47. self.p2.x += x;
49. self.p1.y += y;
50. self.p2.y += y;
51. }
52. }
54. // `Pair` 持有两个分配在堆上的整数
55. struct Pair(Box<i32>, Box<i32>);
57. impl Pair {
58. // 该方法会拿走调用者的所有权
59. // `self` 是 `self: Self` 的语法糖
60. fn destroy(self) {
61. let Pair(first, second) = self;
63. println!("Destroying Pair({}, {})", first, second);
65. // `first` 和 `second` 在这里超出作用域并被释放
66. }
67. }
69. fn main() {
70. let rectangle = Rectangle {
71. // 关联函数的调用不是通过点操作符,而是使用 `::`
72. p1: Point::origin(),
73. p2: Point::new(3.0, 4.0),
74. };
76. // 方法才是通过点操作符调用
77. // 注意,这里的方法需要的是 `&self` 但是我们并没有使用 `(&rectangle).perimeter()` 来调用,原因在于:
78. // 编译器会帮我们自动取引用
79. // `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
80. println!("Rectangle perimeter: {}", rectangle.perimeter());
81. println!("Rectangle area: {}", rectangle.area());
83. let mut square = Rectangle {
84. p1: Point::origin(),
85. p2: Point::new(1.0, 1.0),
86. };
88. // 错误!`rectangle` 是不可变的,但是这个方法要求一个可变的对象
89. //rectangle.translate(1.0, 0.0);
90. // TODO ^ 试着反注释此行,看看会发生什么
92. // 可以!可变对象可以调用可变的方法
93. square.translate(1.0, 1.0);
95. let pair = Pair(Box::new(1), Box::new(2));
97. pair.destroy();
99. // Error! 上一个 `destroy` 调用拿走了 `pair` 的所有权
100. //pair.destroy();
101. // TODO ^ 试着反注释此行
102. }
Exercises
Method
- 🌟🌟 方法跟函数类似:都是使用
fn声明,有参数和返回值。但是与函数不同的是,方法定义在结构体的上下文中(枚举、特征对象也可以定义方法),而且方法的第一个参数一定是self或其变体&self、&mut self,self代表了当前调用的结构体实例。
1. struct Rectangle {
2. width: u32,
3. height: u32,
4. }
6. impl Rectangle {
7. // 完成 area 方法,返回矩形 Rectangle 的面积
8. fn area
9. }
11. fn main() {
12. let rect1 = Rectangle { width: 30, height: 50 };
14. assert_eq!(rect1.area(), 1500);
15. }
- 🌟🌟
self会拿走当前结构体实例(调用对象)的所有权,而&self却只会借用一个不可变引用,&mut self会借用一个可变引用
1. // 只填空,不要删除任何代码行!
2. #[derive(Debug)]
3. struct TrafficLight {
4. color: String,
5. }
7. impl TrafficLight {
8. pub fn show_state(__) {
9. println!("the current state is {}", __.color);
10. }
11. }
12. fn main() {
13. let light = TrafficLight{
14. color: "red".to_owned(),
15. };
16. // 不要拿走 `light` 的所有权
17. light.show_state();
18. // 否则下面代码会报错
19. println!("{:?}", light);
20. }
- 🌟🌟
&self实际上是self: &Self的缩写或者说语法糖
1. struct TrafficLight {
2. color: String,
3. }
5. impl TrafficLight {
6. // 使用 `Self` 填空
7. pub fn show_state(__) {
8. println!("the current state is {}", self.color);
9. }
11. // 填空,不要使用 `Self` 或其变体
12. pub fn change_state(__) {
13. self.color = "green".to_string()
14. }
15. }
16. fn main() {}
Associated function
- 🌟🌟 定义在
impl语句块中的函数被称为关联函数,因为它们跟当前类型关联在一起。关联函数与方法最大的区别就是它第一个参数不是self,原因是它们不需要使用当前的实例,因此关联函数往往可以用于构造函数:初始化一个实例对象。
1. #[derive(Debug)]
2. struct TrafficLight {
3. color: String,
4. }
6. impl TrafficLight {
7. // 1. 实现下面的关联函数 `new`,
8. // 2. 该函数返回一个 TrafficLight 实例,包含 `color` "red"
9. // 3. 该函数必须使用 `Self` 作为类型,不能在签名或者函数体中使用 `TrafficLight`
10. pub fn new()
12. pub fn get_state(&self) -> &str {
13. &self.color
14. }
15. }
17. fn main() {
18. let light = TrafficLight::new();
19. assert_eq!(light.get_state(), "red");
20. }
多个 impl 语句块
- 🌟 每一个结构体允许拥有多个
impl语句块
2. struct Rectangle {
3. width: u32,
4. height: u32,
5. }
7. // 使用多个 `impl` 语句块重写下面的代码
8. impl Rectangle {
9. fn area(&self) -> u32 {
10. self.width * self.height
11. }
13. fn can_hold(&self, other: &Rectangle) -> bool {
14. self.width > other.width && self.height > other.height
15. }
16. }
18. fn main() {}
Enums
- 🌟🌟🌟 我们还可以为枚举类型定义方法
2. #[derive(Debug)]
3. enum TrafficLightColor {
4. Red,
5. Yellow,
6. Green,
7. }
9. // 为 TrafficLightColor 实现所需的方法
10. impl TrafficLightColor {
12. }
14. fn main() {
15. let c = TrafficLightColor::Yellow;
17. assert_eq!(c.color(), "yellow");
19. println!("{:?}",c);
20. }
Practice
@todo
你可以在这里找到答案(在 solutions 路径下)
