1 00:00:06,660 --> 00:00:08,370 - So far we've seen how to define 2 00:00:08,370 --> 00:00:10,200 simple methods in a structure. 3 00:00:10,200 --> 00:00:11,130 Here's an example. 4 00:00:11,130 --> 00:00:13,200 I've got a public structure called Point. 5 00:00:13,200 --> 00:00:15,240 I've defined the implementation block 6 00:00:15,240 --> 00:00:17,190 and I've defined a public function. 7 00:00:17,190 --> 00:00:20,070 It receives a self-reference to indicate 8 00:00:20,070 --> 00:00:21,750 which point it's dealing with, 9 00:00:21,750 --> 00:00:24,360 and then it prints out the X and the Y coordinates 10 00:00:24,360 --> 00:00:25,260 of that point. 11 00:00:25,260 --> 00:00:28,080 So, in your code, if you had a point object, 12 00:00:28,080 --> 00:00:31,143 let's say p1, you'd call the print function, 13 00:00:32,070 --> 00:00:35,910 like so, the compiler internally will convert it 14 00:00:35,910 --> 00:00:39,360 into a call like this print. 15 00:00:39,360 --> 00:00:42,810 It'll pass in a reference to p1 implicitly 16 00:00:42,810 --> 00:00:46,890 and that reference gets passed in to the self parameter. 17 00:00:46,890 --> 00:00:48,990 So the self parameter, a reference, 18 00:00:48,990 --> 00:00:52,230 it knows which object's data it's dealing with. 19 00:00:52,230 --> 00:00:55,800 It's gonna print out the X and the Y coordinates of p1. 20 00:00:55,800 --> 00:00:57,930 So these are called instance methods. 21 00:00:57,930 --> 00:01:00,210 They receive a self parameter 22 00:01:00,210 --> 00:01:01,710 and the self parameter is a reference 23 00:01:01,710 --> 00:01:05,280 to the target object, p1, in this example. 24 00:01:05,280 --> 00:01:07,470 It allows the instance method 25 00:01:07,470 --> 00:01:10,080 to access the state and the target object. 26 00:01:10,080 --> 00:01:15,080 So when we say self.x and self.y, 27 00:01:15,240 --> 00:01:17,670 it knows, the self prompt, that knows 28 00:01:17,670 --> 00:01:19,590 that it's dealing with p1. 29 00:01:19,590 --> 00:01:22,230 And it's going to be the X and Y coordinates of p1 30 00:01:22,230 --> 00:01:23,250 that get displayed. 31 00:01:23,250 --> 00:01:26,400 Okay? So that's quite straightforward. 32 00:01:26,400 --> 00:01:27,990 It's also possible to define 33 00:01:27,990 --> 00:01:30,420 what are called associated functions. 34 00:01:30,420 --> 00:01:31,830 That's a Rust term. 35 00:01:31,830 --> 00:01:34,320 An associated function in Rust is basically the same 36 00:01:34,320 --> 00:01:37,650 as a static method in other object oriented languages. 37 00:01:37,650 --> 00:01:40,200 So an associated function, you define it 38 00:01:40,200 --> 00:01:42,150 in the implementation block, it's still kind of 39 00:01:42,150 --> 00:01:43,530 part of your structure, 40 00:01:43,530 --> 00:01:46,290 part of the concept of your structure 41 00:01:46,290 --> 00:01:49,500 but the function doesn't receive a self parameter. 42 00:01:49,500 --> 00:01:52,620 Okay. And that means it can't access information 43 00:01:52,620 --> 00:01:54,120 in p1 or p2. 44 00:01:54,120 --> 00:01:57,510 It doesn't know anything about any particular instance. 45 00:01:57,510 --> 00:02:00,480 All it knows about is the structure itself. 46 00:02:00,480 --> 00:02:03,960 So associated functions define functionality that pertains 47 00:02:03,960 --> 00:02:07,350 to the whole structure rather than to a particular instance, 48 00:02:07,350 --> 00:02:10,383 like a static method in other OO languages. 49 00:02:11,310 --> 00:02:13,620 Right, well here's an example of how to define 50 00:02:13,620 --> 00:02:14,580 an associated function. 51 00:02:14,580 --> 00:02:17,310 This is actually quite a realistic example. 52 00:02:17,310 --> 00:02:19,650 So I've defined in my types, 53 00:02:19,650 --> 00:02:22,740 I've got a file called Point 3D.rs. 54 00:02:22,740 --> 00:02:26,430 I define a 3D point with an X, Y, Z coordinate. 55 00:02:26,430 --> 00:02:29,850 And it's public. Notice that the fields aren't public now, 56 00:02:29,850 --> 00:02:31,140 that's interesting. 57 00:02:31,140 --> 00:02:34,140 In my implementation block for Point 3D 58 00:02:34,140 --> 00:02:36,870 I've got a public function called new. 59 00:02:36,870 --> 00:02:39,990 There's nothing magical about the new name here, 60 00:02:39,990 --> 00:02:42,120 in Java, and C Sharp, et cetera, 61 00:02:42,120 --> 00:02:43,680 new is a keyword. 62 00:02:43,680 --> 00:02:46,020 In Rust, it isn't. 63 00:02:46,020 --> 00:02:48,180 It's just by convention. 64 00:02:48,180 --> 00:02:50,880 When you write a function to create an instance 65 00:02:50,880 --> 00:02:53,940 and to initialize it, you tend by convention you tend to 66 00:02:53,940 --> 00:02:57,780 name that method new, but it's just a method name. 67 00:02:57,780 --> 00:02:59,400 You could choose any, you could have called 68 00:02:59,400 --> 00:03:02,700 this method name, give me an instance, 69 00:03:02,700 --> 00:03:03,533 and that would've been fine, 70 00:03:03,533 --> 00:03:05,850 but I called it new by convention. 71 00:03:05,850 --> 00:03:09,450 Note this, that it doesn't take a self parameter, okay? 72 00:03:09,450 --> 00:03:13,080 So when you have a method or an associated function 73 00:03:13,080 --> 00:03:15,360 that doesn't take a self parameter 74 00:03:15,360 --> 00:03:19,080 you don't invoke it on an instance, you basically invoke it 75 00:03:19,080 --> 00:03:21,870 on the structure name and we'll do that shortly. 76 00:03:21,870 --> 00:03:23,970 Anyway, it doesn't have a self parameter 77 00:03:23,970 --> 00:03:26,940 but it does have three other parameters. 78 00:03:26,940 --> 00:03:29,040 And what it does is basically a factory, 79 00:03:29,040 --> 00:03:31,650 when you call this method and you pass in some values 80 00:03:31,650 --> 00:03:36,330 it will return an instance of my type, Point 3D, 81 00:03:36,330 --> 00:03:37,470 and that's what it does. 82 00:03:37,470 --> 00:03:41,700 It creates a new Point 3D, and it initializes the X property 83 00:03:41,700 --> 00:03:43,440 with the value of the X parameter 84 00:03:43,440 --> 00:03:46,320 and the Y and the Z, and it returns that Point 3D. 85 00:03:46,320 --> 00:03:48,330 It basically moves ownership 86 00:03:48,330 --> 00:03:50,730 of that Point 3D back into your client code. 87 00:03:50,730 --> 00:03:53,430 So the client code could call this function 88 00:03:53,430 --> 00:03:57,480 to get back an initialized Point 3D object. 89 00:03:57,480 --> 00:04:00,420 So the client code would look like this. 90 00:04:00,420 --> 00:04:03,240 I've imported the structure name, so I've imported 91 00:04:03,240 --> 00:04:06,060 from the top level namespace in my crate. 92 00:04:06,060 --> 00:04:07,740 There's a module called by types 93 00:04:07,740 --> 00:04:10,470 with a submodule called Point 3D 94 00:04:10,470 --> 00:04:13,620 which defines a structure type called Point 3D. 95 00:04:13,620 --> 00:04:15,780 I mean it looks a little bit long-winded 96 00:04:15,780 --> 00:04:18,360 but that is generally what you would do. 97 00:04:18,360 --> 00:04:19,980 So it knows about the structure, 98 00:04:19,980 --> 00:04:22,380 I invoke upon the structure name. 99 00:04:22,380 --> 00:04:25,320 I invoke the associated function. 100 00:04:25,320 --> 00:04:28,762 Very similar to other languages, in C++, 101 00:04:28,762 --> 00:04:31,890 you say class name, colon, colon, 102 00:04:31,890 --> 00:04:35,370 and then the name of the static method you want to invoke. 103 00:04:35,370 --> 00:04:38,790 If this was Java, you'd say class name dot. 104 00:04:38,790 --> 00:04:40,650 And then the name of the static method 105 00:04:40,650 --> 00:04:41,483 you want to call. 106 00:04:41,483 --> 00:04:43,830 The name of the method happens to be new. 107 00:04:43,830 --> 00:04:45,960 But that's just because I chose to call it that. 108 00:04:45,960 --> 00:04:49,200 It's not a keyword, it's up to you what you call the method. 109 00:04:49,200 --> 00:04:51,840 But we've seen this a lot already in Rust, 110 00:04:51,840 --> 00:04:53,670 where you've got some kind of structure, 111 00:04:53,670 --> 00:04:55,260 and you say colon, colon, new, 112 00:04:55,260 --> 00:04:58,800 it creates a new instance based on the data you pass in 113 00:04:58,800 --> 00:05:02,550 it'll give you back a object with those fields set up. 114 00:05:02,550 --> 00:05:04,980 So p1 is a multiple point initially 115 00:05:04,980 --> 00:05:07,830 with those values, 10, 20, 30. 116 00:05:07,830 --> 00:05:10,022 So some points to note, 117 00:05:10,022 --> 00:05:13,800 Point 3D new, we call it a factory function 118 00:05:13,800 --> 00:05:17,130 because when you call that method, it creates an instance, 119 00:05:17,130 --> 00:05:18,423 returns it back to you. 120 00:05:19,800 --> 00:05:23,610 You and your client code receive ownership 121 00:05:23,610 --> 00:05:25,110 of that object that's been returned. 122 00:05:25,110 --> 00:05:27,300 So technically speaking, what happened, 123 00:05:27,300 --> 00:05:29,760 was when you call the new function 124 00:05:29,760 --> 00:05:32,103 it created an object on the stack. 125 00:05:33,180 --> 00:05:37,170 It then moved that object into our local function here 126 00:05:37,170 --> 00:05:41,250 and that move ,it moved ownership of the object to p1. 127 00:05:41,250 --> 00:05:44,100 Okay, so effectively p1 is the object 128 00:05:44,100 --> 00:05:45,750 that's been created and returned. 129 00:05:46,860 --> 00:05:48,660 Right, well, let's have a look at the example 130 00:05:48,660 --> 00:05:51,750 in lesson11_structs_functionality. 131 00:05:51,750 --> 00:05:54,600 From the top, we'll have main.rs, 132 00:05:54,600 --> 00:05:57,930 which imports our mytypes module. 133 00:05:57,930 --> 00:06:01,830 In the mytypes module, I declare three sub modules, 134 00:06:01,830 --> 00:06:04,380 one of which is point3d. 135 00:06:04,380 --> 00:06:07,110 And in there I define my Point 3D structure. 136 00:06:07,110 --> 00:06:10,290 And then I use it in this file here. 137 00:06:10,290 --> 00:06:11,280 So we'll have a look at the code 138 00:06:11,280 --> 00:06:12,930 and then we'll run it at the end. 139 00:06:12,930 --> 00:06:13,923 Let's have a look. 140 00:06:14,970 --> 00:06:19,600 Right, so from the top, define a module called mytypes. 141 00:06:20,580 --> 00:06:22,770 Mytypes defines submodules. 142 00:06:22,770 --> 00:06:24,480 I should click here actually. 143 00:06:24,480 --> 00:06:28,140 It defines a submodule called point3d. 144 00:06:28,140 --> 00:06:30,720 Okay? So if we're in a file called mytypes 145 00:06:30,720 --> 00:06:32,400 and it defines a submodule, 146 00:06:32,400 --> 00:06:34,480 it'll go to the mytypes subdirectory 147 00:06:35,340 --> 00:06:40,340 and in there it'll find point3d.rs. 148 00:06:40,920 --> 00:06:41,970 Here it is. 149 00:06:41,970 --> 00:06:45,000 So it defines my 3D point structure. 150 00:06:45,000 --> 00:06:47,280 I think this is pretty much the same 151 00:06:47,280 --> 00:06:48,780 as what we've seen before. 152 00:06:48,780 --> 00:06:49,903 Let me just run through it. 153 00:06:49,903 --> 00:06:53,190 A 3D point with private fields 154 00:06:53,190 --> 00:06:56,940 that means we're not accessing those fields externally. 155 00:06:56,940 --> 00:06:59,130 And we have an instance method 156 00:06:59,130 --> 00:07:02,163 which takes a multiple reference to self, and prints it. 157 00:07:03,030 --> 00:07:06,720 We have another instance method, which takes an immutable, 158 00:07:06,720 --> 00:07:09,000 that's an immutable reference to self. 159 00:07:09,000 --> 00:07:10,680 This one receives an immutable reference 160 00:07:10,680 --> 00:07:13,920 and returns a string, a formatted string. 161 00:07:13,920 --> 00:07:15,510 And we've seen these before. 162 00:07:15,510 --> 00:07:19,620 Another instance method that receives a self parameter. 163 00:07:19,620 --> 00:07:21,420 This is a mutable method. 164 00:07:21,420 --> 00:07:25,350 It'll reset the coordinate to an origin, basically zero. 165 00:07:25,350 --> 00:07:26,670 A move by function. 166 00:07:26,670 --> 00:07:30,480 That's also an instance method. 167 00:07:30,480 --> 00:07:32,100 It has a self parameter. 168 00:07:32,100 --> 00:07:35,790 It can mutate the object with deltas. 169 00:07:35,790 --> 00:07:38,040 So it'll offset the X and the Y and the Z 170 00:07:38,040 --> 00:07:39,990 by the specified delta. 171 00:07:39,990 --> 00:07:42,120 Another instance method, 172 00:07:42,120 --> 00:07:43,620 most of the methods you have in the class 173 00:07:43,620 --> 00:07:44,640 are instance methods. 174 00:07:44,640 --> 00:07:47,520 They work on an actual instance. 175 00:07:47,520 --> 00:07:49,800 So the log method, we've seen that already. 176 00:07:49,800 --> 00:07:52,350 It'll take an immutable reference to self 177 00:07:52,350 --> 00:07:54,240 and it'll return a nicely formatted string 178 00:07:54,240 --> 00:07:55,950 with a timestamp. 179 00:07:55,950 --> 00:07:57,690 And then finally at the bottom here 180 00:07:57,690 --> 00:07:59,340 is our associated function. 181 00:07:59,340 --> 00:08:02,730 So in order to differentiate an associated function 182 00:08:02,730 --> 00:08:06,450 from a regular method, it's the absence of a self parameter. 183 00:08:06,450 --> 00:08:07,800 That's the only difference. 184 00:08:07,800 --> 00:08:09,660 It doesn't have a self parameter. 185 00:08:09,660 --> 00:08:12,990 It can't access anything on the current object. 186 00:08:12,990 --> 00:08:16,470 Instead, it creates a new object and then returns it. 187 00:08:16,470 --> 00:08:18,450 So remember here I could have explicitly 188 00:08:18,450 --> 00:08:21,300 had a return statement and I could have said, 189 00:08:21,300 --> 00:08:24,360 set the X field to be the value of the X parameter 190 00:08:24,360 --> 00:08:25,590 because the parameter happens to 191 00:08:25,590 --> 00:08:27,450 have the same name as the field. 192 00:08:27,450 --> 00:08:31,200 Set the Y field to be the value of the Y parameter, 193 00:08:31,200 --> 00:08:34,653 set the Z field to be the value of the Z parameter. 194 00:08:35,790 --> 00:08:38,010 Z and semicolon. 195 00:08:38,010 --> 00:08:39,720 I could have done that, 196 00:08:39,720 --> 00:08:41,970 but you just have an expression 197 00:08:41,970 --> 00:08:43,770 as an implicit return statement. 198 00:08:43,770 --> 00:08:46,500 And if the field name, we've seen this already, 199 00:08:46,500 --> 00:08:49,410 if the field name is the same as the local variable 200 00:08:49,410 --> 00:08:52,350 then you can just use this kind of shortened syntax 201 00:08:52,350 --> 00:08:54,513 and the compiler knows what you mean. 202 00:08:55,410 --> 00:08:59,640 All right, so that's my associated function. 203 00:08:59,640 --> 00:09:01,260 And oh, let's go to the main 204 00:09:01,260 --> 00:09:03,510 because we need to actually call this method. 205 00:09:04,890 --> 00:09:09,213 So demo_associated_functions, and it's here. 206 00:09:10,530 --> 00:09:15,240 Okay, so I use my factory method to create a new instance. 207 00:09:15,240 --> 00:09:17,610 So this is starting to look quite similar 208 00:09:17,610 --> 00:09:19,200 to types we've seen before. 209 00:09:19,200 --> 00:09:22,350 Where you say type name colon, colon, new. 210 00:09:22,350 --> 00:09:24,240 Okay, just to emphasize the point 211 00:09:24,240 --> 00:09:27,240 there is nothing magical about this name here. 212 00:09:27,240 --> 00:09:30,852 What I could have done is I could have renamed that method 213 00:09:30,852 --> 00:09:35,852 gimme_point3d_dude, okay, 214 00:09:37,950 --> 00:09:41,130 that would've been slightly less professional, but doable. 215 00:09:41,130 --> 00:09:43,680 And then I could have invoked the method like that. 216 00:09:43,680 --> 00:09:47,220 Okay? But not as conventional as just saying new. 217 00:09:47,220 --> 00:09:51,753 So let's just revert that and just revert that to be new. 218 00:09:52,890 --> 00:09:55,230 Okay, so anyway, whatever it's called, invoke that method 219 00:09:55,230 --> 00:09:58,350 it returns back a point object, which we now own. 220 00:09:58,350 --> 00:10:00,090 Oh, I've declared it mutable. 221 00:10:00,090 --> 00:10:03,000 So I can move it, move it, move it 222 00:10:03,000 --> 00:10:05,430 and I can reset it like so. 223 00:10:05,430 --> 00:10:06,990 So we just need to run the application now 224 00:10:06,990 --> 00:10:09,330 and bask in the glory. 225 00:10:09,330 --> 00:10:10,773 I hope that it's gonna work. 226 00:10:11,910 --> 00:10:13,473 Cargo run. 227 00:10:16,140 --> 00:10:18,150 Fantastic. So created the point object 228 00:10:18,150 --> 00:10:19,560 using the associated function. 229 00:10:19,560 --> 00:10:21,630 You can have other associated functions as well. 230 00:10:21,630 --> 00:10:24,180 Any function that doesn't need a self parameter 231 00:10:24,180 --> 00:10:26,130 would have the same kind of pattern. 232 00:10:26,130 --> 00:10:27,600 I moved it. 233 00:10:27,600 --> 00:10:30,150 When you call any kinda mutator in my demo 234 00:10:30,150 --> 00:10:32,070 it outputs a log message first, 235 00:10:32,070 --> 00:10:34,110 saying we're about to move the object. 236 00:10:34,110 --> 00:10:35,580 And then we did. 237 00:10:35,580 --> 00:10:36,843 And then when I reset it, 238 00:10:37,711 --> 00:10:39,270 the log method will say, 239 00:10:39,270 --> 00:10:41,160 I'm about to reset it, and it does. 240 00:10:41,160 --> 00:10:42,900 And then we print the string. 241 00:10:42,900 --> 00:10:47,220 So I'm about to reset the point, reset the point 242 00:10:47,220 --> 00:10:48,810 and then that's the value afterwards. 243 00:10:48,810 --> 00:10:51,720 Okay. So that was really quite useful, I think. 244 00:10:51,720 --> 00:10:54,510 So the key point then is associated functions, 245 00:10:54,510 --> 00:10:55,770 basically static functions. 246 00:10:55,770 --> 00:10:58,410 There is no static keyword by the way in Rust 247 00:10:58,410 --> 00:10:59,700 but an associated function, 248 00:10:59,700 --> 00:11:01,800 part of the concept of the structure, 249 00:11:01,800 --> 00:11:03,690 but it doesn't have a self parameter. 250 00:11:03,690 --> 00:11:06,270 Okay? So it operates at the structure level 251 00:11:06,270 --> 00:11:08,320 rather than at the actual instance level.