Associated functions & Methods
Examples
1. struct Point {
2. x: f64,
3. y: f64,
4. }
6. // Implementation block, all `Point` associated functions & methods go in here.
7. impl Point {
8. // This is an "associated function" because this function is associated with
9. // a particular type, that is, Point.
10. //
11. // Associated functions don't need to be called with an instance.
12. // These functions are generally used like constructors.
13. fn origin() -> Point {
14. Point { x: 0.0, y: 0.0 }
15. }
17. // Another associated function, taking two arguments:
18. fn new(x: f64, y: f64) -> Point {
19. Point { x: x, y: y }
20. }
21. }
23. struct Rectangle {
24. p1: Point,
25. p2: Point,
26. }
28. impl Rectangle {
29. // This is a method.
30. // `&self` is sugar for `self: &Self`, where `Self` is the type of the
31. // caller object. In this case `Self` = `Rectangle`
32. fn area(&self) -> f64 {
33. // `self` gives access to the struct fields via the dot operator.
34. let Point { x: x1, y: y1 } = self.p1;
35. let Point { x: x2, y: y2 } = self.p2;
37. // `abs` is a `f64` method that returns the absolute value of the
38. // caller
39. ((x1 - x2) * (y1 - y2)).abs()
40. }
42. fn perimeter(&self) -> f64 {
43. let Point { x: x1, y: y1 } = self.p1;
44. let Point { x: x2, y: y2 } = self.p2;
46. 2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
47. }
49. // This method requires the caller object to be mutable
50. // `&mut self` desugars to `self: &mut Self`
51. fn translate(&mut self, x: f64, y: f64) {
52. self.p1.x += x;
53. self.p2.x += x;
55. self.p1.y += y;
56. self.p2.y += y;
57. }
58. }
60. // `Pair` owns resources: two heap allocated integers.
61. struct Pair(Box<i32>, Box<i32>);
63. impl Pair {
64. // This method "consumes" the resources of the caller object
65. // `self` desugars to `self: Self`
66. fn destroy(self) {
67. // Destructure `self`
68. let Pair(first, second) = self;
70. println!("Destroying Pair({}, {})", first, second);
72. // `first` and `second` go out of scope and get freed.
73. }
74. }
76. fn main() {
77. let rectangle = Rectangle {
78. // Associated functions are called using double colons
79. p1: Point::origin(),
80. p2: Point::new(3.0, 4.0),
81. };
83. // Methods are called using the dot operator.
84. // Note that the first argument `&self` is implicitly passed, i.e.
85. // `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
86. println!("Rectangle perimeter: {}", rectangle.perimeter());
87. println!("Rectangle area: {}", rectangle.area());
89. let mut square = Rectangle {
90. p1: Point::origin(),
91. p2: Point::new(1.0, 1.0),
92. };
94. // Error! `rectangle` is immutable, but this method requires a mutable
95. // object.
96. //rectangle.translate(1.0, 0.0);
97. // TODO ^ Try uncommenting this line
99. // Okay! Mutable objects can call mutable methods
100. square.translate(1.0, 1.0);
102. let pair = Pair(Box::new(1), Box::new(2));
104. pair.destroy();
106. // Error! Previous `destroy` call "consumed" `pair`
107. //pair.destroy();
108. // TODO ^ Try uncommenting this line
109. }
Exercises
Method
- 🌟🌟 Methods are similar to functions: Declare with
fn, have parameters and a return value. Unlike functions, methods are defined within the context of a struct (or an enum or a trait object), and their first parameter is alwaysself, which represents the instance of the struct the method is being called on.
1. struct Rectangle {
2. width: u32,
3. height: u32,
4. }
6. impl Rectangle {
7. // Complete the area method which return the area of a Rectangle.
8. fn area
9. }
11. fn main() {
12. let rect1 = Rectangle { width: 30, height: 50 };
14. assert_eq!(rect1.area(), 1500);
16. println!("Success!");
17. }
- 🌟🌟
selfwill take the ownership of current struct instance, however,&selfwill only borrow a reference from the instance.
1. // Only fill in the blanks, DON'T remove any line!
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. // Don't take the ownership of `light` here.
17. light.show_state();
18. // ... Otherwise, there will be an error below
19. println!("{:?}", light);
20. }
- 🌟🌟 The
&selfis actually short forself: &Self. Within animplblock, the typeSelfis an alias for the type that theimplblock is for. Methods must have a parameter namedselfof typeSelffor their first parameter, so Rust lets you abbreviate this with only the nameselfin the first parameter spot.
1. struct TrafficLight {
2. color: String,
3. }
5. impl TrafficLight {
6. // Using `Self` to fill in the blank.
7. pub fn show_state(__) {
8. println!("the current state is {}", self.color);
9. }
11. // Fill in the blank, DON'T use any variants of `Self`.
12. pub fn change_state(__) {
13. self.color = "green".to_string()
14. }
15. }
16. fn main() {
17. println!("Success!");
18. }
Associated functions
- 🌟🌟 All functions defined within an
implblock are called associated functions because they’re associated with the type named after theimpl. We can define associated functions that don’t haveselfas their first parameter (and thus are not methods) because they don’t need an instance of the type to work with.
1. #[derive(Debug)]
2. struct TrafficLight {
3. color: String,
4. }
6. impl TrafficLight {
7. // 1. Implement an associated function `new`,
8. // 2. It will return a TrafficLight contains color "red"
9. // 3. Must use `Self`, DONT use `TrafficLight` in fn signatures or body
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");
21. println!("Success!");
22. }
Multiple impl blocks
- 🌟 Each struct is allowed to have multiple impl blocks.
2. struct Rectangle {
3. width: u32,
4. height: u32,
5. }
7. // Using multiple `impl` blocks to rewrite the code below.
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() {
19. println!("Success!");
20. }
Enums
- 🌟🌟🌟 We can also implement methods for enums.
2. #[derive(Debug)]
3. enum TrafficLightColor {
4. Red,
5. Yellow,
6. Green,
7. }
9. // Implement TrafficLightColor with a method.
10. impl TrafficLightColor {
12. }
14. fn main() {
15. let c = TrafficLightColor::Yellow;
17. assert_eq!(c.color(), "yellow");
19. println!("{:?}",c);
20. }
Practice
@todo
You can find the solutions here(under the solutions path), but only use it when you need it
