1 00:00:06,610 --> 00:00:09,440 - Alright, so we just looked at the base mechanics 2 00:00:09,440 --> 00:00:12,050 for value and pointer semantics. 3 00:00:12,050 --> 00:00:13,800 These are going to be very important going forward. 4 00:00:13,800 --> 00:00:16,330 Again, I want to make sure that we can read code 5 00:00:16,330 --> 00:00:17,590 in Go and understand the cost 6 00:00:17,590 --> 00:00:19,330 and the impact that code is having. 7 00:00:19,330 --> 00:00:22,130 Memory is going to be a big part of that. 8 00:00:22,130 --> 00:00:25,127 Now, there's a big thing here around these semantics, 9 00:00:25,127 --> 00:00:27,000 the value semantics and the pointer semantics, 10 00:00:27,000 --> 00:00:28,380 and it's the cognitive load 11 00:00:28,380 --> 00:00:30,800 around understanding how to manage memory. 12 00:00:30,800 --> 00:00:33,360 One of the most beautiful things about Go 13 00:00:33,360 --> 00:00:36,040 is it takes that burden off of us. 14 00:00:36,040 --> 00:00:37,860 We don't necessarily have to worry about things 15 00:00:37,860 --> 00:00:41,880 'cause we make a mistake with where things are located 16 00:00:41,880 --> 00:00:43,190 and how we access it, it can cause 17 00:00:43,190 --> 00:00:45,330 very very big problems for us. 18 00:00:45,330 --> 00:00:47,600 So our value and our pointer semantics are gonna be big 19 00:00:47,600 --> 00:00:49,610 for us, and we're gonna be able to read and understand 20 00:00:49,610 --> 00:00:52,950 how things behave, and again, let's talk about that again. 21 00:00:52,950 --> 00:00:56,240 Value semantics have the benefit of being able 22 00:00:56,240 --> 00:01:00,020 to mutate memory and isolation within our own sandboxes, 23 00:01:00,020 --> 00:01:02,210 but it has the cost of inefficiency. 24 00:01:02,210 --> 00:01:04,250 We're gonna have lost a copy of the data 25 00:01:04,250 --> 00:01:07,150 as we cross over these program boundaries. 26 00:01:07,150 --> 00:01:11,640 Pointer semantics, however, fix the efficiency issue. 27 00:01:11,640 --> 00:01:13,040 We're just gonna have one piece of data, 28 00:01:13,040 --> 00:01:15,090 we're gonna share it, everybody can mutate it, 29 00:01:15,090 --> 00:01:16,640 everybody's gonna see it, but it comes 30 00:01:16,640 --> 00:01:19,370 with the cost of side effects and a lot more work 31 00:01:19,370 --> 00:01:22,340 for us to make sure that we're not corrupting data 32 00:01:22,340 --> 00:01:25,370 or things aren't being changed behind the scenes 33 00:01:25,370 --> 00:01:28,687 without a piece of code or a Go routine later on knowing it. 34 00:01:28,687 --> 00:01:30,350 And this is complex stuff, 35 00:01:30,350 --> 00:01:32,280 I'm just not gonna hide it for you. 36 00:01:32,280 --> 00:01:34,052 But, if we balance our value 37 00:01:34,052 --> 00:01:36,080 and our pointer semantics properly, 38 00:01:36,080 --> 00:01:38,050 leveraging the aspects of the language 39 00:01:38,050 --> 00:01:40,610 helping the cognitive load over memory management, 40 00:01:40,610 --> 00:01:42,760 it's going to be a lot better for us. 41 00:01:42,760 --> 00:01:45,600 So I want to show you another example here 42 00:01:45,600 --> 00:01:49,480 of these semantics, and how the language is really here 43 00:01:49,480 --> 00:01:51,490 helping some of our cognitive load. 44 00:01:51,490 --> 00:01:54,040 This is also gonna be important in our ability 45 00:01:54,040 --> 00:01:55,960 to understand the impact and cost 46 00:01:55,960 --> 00:01:58,160 our code is having on the machine. 47 00:01:58,160 --> 00:01:59,930 So I've got a user defined type here. 48 00:01:59,930 --> 00:02:02,220 It's username and email, and what I do 49 00:02:02,220 --> 00:02:05,520 is I have two functions, one called create user v one, 50 00:02:05,520 --> 00:02:07,610 and one called create user v two. 51 00:02:07,610 --> 00:02:12,290 Now, as I shared before, we don't have constructors in Go. 52 00:02:12,290 --> 00:02:13,960 Yay, we don't want that, right? 53 00:02:13,960 --> 00:02:15,930 It hides cost, but what we do have 54 00:02:15,930 --> 00:02:19,030 is what I call factory functions. 55 00:02:19,030 --> 00:02:22,580 Factory function is a function that creates a value, 56 00:02:22,580 --> 00:02:26,210 initializes it for use, and returns it back to the caller. 57 00:02:26,210 --> 00:02:29,680 This is great for readability, it doesn't hide cost, 58 00:02:29,680 --> 00:02:33,120 we can read it, and lends to simplicity 59 00:02:33,120 --> 00:02:34,770 in terms of construction. 60 00:02:34,770 --> 00:02:36,300 Now I don't want to call these functions 61 00:02:36,300 --> 00:02:38,280 constructors because that's baggage. 62 00:02:38,280 --> 00:02:40,100 I call them factory functions. 63 00:02:40,100 --> 00:02:42,960 And I've got two factory functions in this piece of code, 64 00:02:42,960 --> 00:02:45,830 version one and version two of the create user. 65 00:02:45,830 --> 00:02:49,640 Let's start with version one of the factory function. 66 00:02:49,640 --> 00:02:51,850 Now the first thing I want you to do is look 67 00:02:51,850 --> 00:02:54,400 at the return type for the function. 68 00:02:54,400 --> 00:02:57,890 It returns a value of type user. 69 00:02:57,890 --> 00:03:01,270 Immediately that clues us into the understanding 70 00:03:01,270 --> 00:03:04,370 that this factory function is using value semantics. 71 00:03:04,370 --> 00:03:05,650 Throughout this class, I'm going 72 00:03:05,650 --> 00:03:09,540 to ask you what semantic is in play. 73 00:03:09,540 --> 00:03:12,310 This is critical to everything as you will see. 74 00:03:12,310 --> 00:03:17,190 And when we talk about v one, value semantics are in play 75 00:03:17,190 --> 00:03:20,630 because the caller's going to get their own copy 76 00:03:20,630 --> 00:03:23,660 of the value after this function returned. 77 00:03:23,660 --> 00:03:25,810 Now if we were to map out visually 78 00:03:25,810 --> 00:03:29,090 how this function's behaving, we would probably think 79 00:03:29,090 --> 00:03:30,653 that this is what's going on. 80 00:03:32,250 --> 00:03:35,450 We're in main, now we call v one. 81 00:03:35,450 --> 00:03:37,260 There it is, version one. 82 00:03:37,260 --> 00:03:39,130 And right away in this function 83 00:03:39,130 --> 00:03:43,300 on line 25 through 28, we have literal construction. 84 00:03:43,300 --> 00:03:46,430 We're constructing a value of type user, 85 00:03:46,430 --> 00:03:48,640 using our literal construction. 86 00:03:48,640 --> 00:03:52,690 We end up with a variable named u and a value 87 00:03:52,690 --> 00:03:55,070 that has at least Bill inside of it. 88 00:03:55,070 --> 00:03:57,380 There it is, we're seeing construction, 89 00:03:57,380 --> 00:03:59,780 and right now, I'm drawing it within the frame 90 00:03:59,780 --> 00:04:01,100 that the active goroutine. 91 00:04:01,100 --> 00:04:03,090 Here's our active frame at this point. 92 00:04:03,090 --> 00:04:06,253 Our goroutine is operating within this frame right here 93 00:04:06,253 --> 00:04:08,030 when we do the construction. 94 00:04:08,030 --> 00:04:09,640 Now I want you to look on line 30. 95 00:04:09,640 --> 00:04:12,070 The ampersand operator is very powerful 96 00:04:12,070 --> 00:04:14,220 from a readability standpoint. 97 00:04:14,220 --> 00:04:18,250 Ampersand means sharing, and now when I read line 30, 98 00:04:18,250 --> 00:04:23,210 what I see is we are sharing the user value 99 00:04:23,210 --> 00:04:25,790 down the call stack to the print function. 100 00:04:25,790 --> 00:04:29,000 At that point, we have another frame for print, 101 00:04:29,000 --> 00:04:31,510 and we see here that we are 102 00:04:31,510 --> 00:04:35,170 sharing that value down the call stack. 103 00:04:35,170 --> 00:04:37,530 Now when print wants to display it, 104 00:04:37,530 --> 00:04:39,840 it's gonna do it through an indirect pointer here, 105 00:04:39,840 --> 00:04:41,440 it's doing it through indirection, 106 00:04:41,440 --> 00:04:45,370 but it's okay because when this is the active frame, 107 00:04:45,370 --> 00:04:48,520 remember all of the memory associated 108 00:04:48,520 --> 00:04:51,530 with the active frame up is valid. 109 00:04:51,530 --> 00:04:54,810 This is perfectly clean to share that pointer down. 110 00:04:54,810 --> 00:04:55,850 Life is good. 111 00:04:55,850 --> 00:04:59,280 Eventually, this is going to return. 112 00:04:59,280 --> 00:05:01,440 This is gonna be the active frame again. 113 00:05:01,440 --> 00:05:04,080 Our goroutine will operate on here. 114 00:05:04,080 --> 00:05:05,070 Life is good. 115 00:05:05,070 --> 00:05:09,290 And then on line 32, notice that we're doing a return u. 116 00:05:09,290 --> 00:05:11,740 This is the value semantic call, right? 117 00:05:11,740 --> 00:05:15,640 We are making a copy of u and passing it back up 118 00:05:15,640 --> 00:05:18,910 across our program boundary back up the call stack, 119 00:05:18,910 --> 00:05:21,000 which now means that when this goroutine 120 00:05:21,000 --> 00:05:25,050 is now operating here, here's our active frame, 121 00:05:25,050 --> 00:05:27,180 here's our goroutine right here. 122 00:05:27,180 --> 00:05:30,900 This goroutine now is operating on its own copy of u, 123 00:05:30,900 --> 00:05:32,690 there it is, Bill. 124 00:05:32,690 --> 00:05:35,420 Which is fine because, again, 125 00:05:35,420 --> 00:05:36,770 that memory that we were using 126 00:05:36,770 --> 00:05:39,520 below the active frame, we didn't need it anyway 127 00:05:39,520 --> 00:05:42,050 because we're operating on our own copy. 128 00:05:42,050 --> 00:05:45,877 This is classic classic value semantics, 129 00:05:45,877 --> 00:05:48,570 and you can see the levels of integrity we get 130 00:05:48,570 --> 00:05:51,220 because the goroutine is operating on its own copy. 131 00:05:51,220 --> 00:05:53,080 It's gonna operate on its own copy. 132 00:05:53,080 --> 00:05:54,187 We did make a copy of it, 133 00:05:54,187 --> 00:05:56,250 but this is now the point of truth. 134 00:05:56,250 --> 00:05:58,690 We don't care about anything that's happening below. 135 00:05:58,690 --> 00:06:02,190 Again, we can't worry about this data here, 136 00:06:02,190 --> 00:06:06,620 and it doesn't matter because when main makes another call, 137 00:06:06,620 --> 00:06:08,773 it's gonna wipe out all of this data. 138 00:06:08,773 --> 00:06:11,350 This is all basically ready for use again. 139 00:06:11,350 --> 00:06:14,650 But we're seeing our value semantics pretty strong here. 140 00:06:14,650 --> 00:06:18,750 Now let's look at version two of this piece of code. 141 00:06:18,750 --> 00:06:22,070 If you notice here, version two, look at the return, 142 00:06:22,070 --> 00:06:24,980 it's not using value semantics anymore. 143 00:06:24,980 --> 00:06:28,660 Version two is using pointer semantics. 144 00:06:28,660 --> 00:06:31,660 Notice what it's saying is it's going to give the caller 145 00:06:31,660 --> 00:06:34,640 a copy of the address that we are constructing. 146 00:06:34,640 --> 00:06:37,120 This is very common stuff to do in Go. 147 00:06:37,120 --> 00:06:41,290 Let's draw out what this looks like on the board 148 00:06:41,290 --> 00:06:43,950 and get a sense of what's happening here. 149 00:06:43,950 --> 00:06:46,363 So, I'm gonna clean up my stack right here. 150 00:06:47,430 --> 00:06:49,800 We're gonna have our main right here. 151 00:06:49,800 --> 00:06:51,700 We're gonna have version two. 152 00:06:51,700 --> 00:06:56,270 Now, this code looks identical to version one. 153 00:06:56,270 --> 00:06:57,410 There is a couple of subtleties, 154 00:06:57,410 --> 00:06:59,480 but it really kind of looks identical. 155 00:06:59,480 --> 00:07:02,300 Look on line 39 through 42, it looks 156 00:07:02,300 --> 00:07:05,240 like we're performing the same exact construction. 157 00:07:05,240 --> 00:07:07,600 It looks like we're constructing a value 158 00:07:07,600 --> 00:07:10,550 of type user, initializing it to Bill. 159 00:07:10,550 --> 00:07:13,140 We're sharing it down the call stack again. 160 00:07:13,140 --> 00:07:15,720 Again here, this is our active frame, 161 00:07:15,720 --> 00:07:18,200 our goroutine is operating right here. 162 00:07:18,200 --> 00:07:21,940 We share it down the call stack again like we saw before. 163 00:07:21,940 --> 00:07:23,200 We're gonna share it. 164 00:07:23,200 --> 00:07:25,850 The active frame was here, we come back up. 165 00:07:25,850 --> 00:07:28,400 But this time, on the return, 166 00:07:28,400 --> 00:07:30,770 we're not making a copy of the value. 167 00:07:30,770 --> 00:07:34,170 We're making a copy of the value's address. 168 00:07:34,170 --> 00:07:36,510 Again pass by value where the data 169 00:07:36,510 --> 00:07:38,600 is either a value or an address. 170 00:07:38,600 --> 00:07:42,460 Now it looks like, from reading the code on the surface, 171 00:07:42,460 --> 00:07:46,050 this is what we're gonna end up with right here. 172 00:07:46,050 --> 00:07:48,240 Now, if this was truly the case, 173 00:07:48,240 --> 00:07:52,920 we would be in a tremendous amount of trouble. 174 00:07:52,920 --> 00:07:56,410 Think about what I've just drawn on the board. 175 00:07:56,410 --> 00:07:59,990 As we perform the return and share the value 176 00:07:59,990 --> 00:08:04,000 up the call stack and once this becomes the active frame, 177 00:08:04,000 --> 00:08:07,600 this is a very very bad scenario. 178 00:08:07,600 --> 00:08:12,440 If this was really happening, we'd have an integrity issue 179 00:08:12,440 --> 00:08:16,340 because when main makes the next function call, 180 00:08:16,340 --> 00:08:18,471 it's gonna take a frame, 181 00:08:18,471 --> 00:08:20,310 and it's gonna wipe all of that out. 182 00:08:20,310 --> 00:08:22,290 Oh my goodness, think about it. 183 00:08:22,290 --> 00:08:24,130 When we share up the call stack, 184 00:08:24,130 --> 00:08:26,670 what we're doing is basically putting ourselves 185 00:08:26,670 --> 00:08:29,290 in a situation where the pointer is pointing 186 00:08:29,290 --> 00:08:31,830 to a piece of data that can be corrupted. 187 00:08:31,830 --> 00:08:33,370 Side effects, oh my god, 188 00:08:33,370 --> 00:08:35,830 this stuff gets very very complicated. 189 00:08:35,830 --> 00:08:39,580 So this absolutely cannot be what is happening, 190 00:08:39,580 --> 00:08:43,790 and it's not, and if we didn't have the compiler coming in 191 00:08:43,790 --> 00:08:46,380 and dealing with this, this is something that you 192 00:08:46,380 --> 00:08:48,470 would have to deal with as a developer, 193 00:08:48,470 --> 00:08:50,040 which is why it's so complicated, 194 00:08:50,040 --> 00:08:52,320 and we've made so many mistakes in the past. 195 00:08:52,320 --> 00:08:54,240 So, what's happening here is the compiler 196 00:08:54,240 --> 00:08:56,380 is really really powerful. 197 00:08:56,380 --> 00:08:59,800 The compiler is able to perform static code analysis, 198 00:08:59,800 --> 00:09:02,100 and in this case what the compiler will do is perform 199 00:09:02,100 --> 00:09:05,010 what's called escape analysis. 200 00:09:05,010 --> 00:09:08,210 And this static code analysis called escape analysis 201 00:09:08,210 --> 00:09:11,110 will determine whether a value gets to be placed 202 00:09:11,110 --> 00:09:13,410 on the stack, which is what we want, 203 00:09:13,410 --> 00:09:16,050 or it escapes to the heap. 204 00:09:16,050 --> 00:09:18,290 Notice that our first priority is 205 00:09:18,290 --> 00:09:20,650 that a value stays on the stack. 206 00:09:20,650 --> 00:09:23,980 And this is because that memory is already there. 207 00:09:23,980 --> 00:09:27,450 It's very very fast to leverage the stack. 208 00:09:27,450 --> 00:09:30,050 Also stacks are self-cleaning, 209 00:09:30,050 --> 00:09:32,050 which means that the garbage collector 210 00:09:32,050 --> 00:09:34,300 doesn't even get involved until a value 211 00:09:34,300 --> 00:09:36,680 escapes a stack and ends up on the heap. 212 00:09:36,680 --> 00:09:39,430 And in Go, that is what we call an allocation. 213 00:09:39,430 --> 00:09:43,240 An allocation in Go is when an escape analysis determines 214 00:09:43,240 --> 00:09:45,840 that a value cannot be constructed on the stack, 215 00:09:45,840 --> 00:09:47,900 but has to be constructed on the heap 216 00:09:47,900 --> 00:09:49,520 because to keep it on the stack, 217 00:09:49,520 --> 00:09:53,120 like in this particular case would have an integrity issue. 218 00:09:53,120 --> 00:09:55,410 And now the garbage collector has to get involved 219 00:09:55,410 --> 00:09:58,447 with anything that allocates to the heap. 220 00:09:58,447 --> 00:10:01,410 Now you might be saying but why does the garbage collector 221 00:10:01,410 --> 00:10:02,660 not have to deal with stacks. 222 00:10:02,660 --> 00:10:04,950 How are stacks self cleaning? 223 00:10:04,950 --> 00:10:07,880 Let's go back to zero value for a second. 224 00:10:07,880 --> 00:10:10,050 Remember every time we make a function call, 225 00:10:10,050 --> 00:10:12,830 we take a frame and we clean it. 226 00:10:12,830 --> 00:10:16,030 Zero value helps to make sure that every single byte 227 00:10:16,030 --> 00:10:18,630 and every single bit is set to where it's supposed to be. 228 00:10:18,630 --> 00:10:20,290 There's nothing left random. 229 00:10:20,290 --> 00:10:23,050 You make another function call, what happens? 230 00:10:23,050 --> 00:10:26,020 You make another function call, we take another frame, 231 00:10:26,020 --> 00:10:28,930 we clean it, take another frame, you clean it. 232 00:10:28,930 --> 00:10:30,710 See, stacks are self-cleaning 233 00:10:30,710 --> 00:10:32,550 because as you make function calls, 234 00:10:32,550 --> 00:10:34,900 the stack is being cleaned all the way down, 235 00:10:34,900 --> 00:10:37,190 but as we've come back up and returned, 236 00:10:37,190 --> 00:10:38,600 we leave that memory. 237 00:10:38,600 --> 00:10:40,810 It would be inefficient to clean memory 238 00:10:40,810 --> 00:10:42,720 on the way up because we don't really know 239 00:10:42,720 --> 00:10:43,710 if we need it again. 240 00:10:43,710 --> 00:10:46,390 We have to have some levels of efficiency. 241 00:10:46,390 --> 00:10:49,660 So memory is left alone on the stack as we go up 242 00:10:49,660 --> 00:10:53,270 and cleaned on the way down, so the garbage collector 243 00:10:53,270 --> 00:10:54,250 doesn't have to get involved. 244 00:10:54,250 --> 00:10:56,590 The stack can give us a tremendous amount 245 00:10:56,590 --> 00:10:58,990 of performance because the memory 246 00:10:58,990 --> 00:11:02,000 is already allocated and it's self-cleaning. 247 00:11:02,000 --> 00:11:04,450 We really want to try our best 248 00:11:04,450 --> 00:11:06,670 to leverage our value semantics 249 00:11:06,670 --> 00:11:09,200 and to keep values on the stack 250 00:11:09,200 --> 00:11:10,720 because again, one of the benefits 251 00:11:10,720 --> 00:11:13,060 of not only the isolation and the immutability 252 00:11:13,060 --> 00:11:14,620 and the reducing our side effects, 253 00:11:14,620 --> 00:11:17,380 but in many cases can also give us better performance 254 00:11:17,380 --> 00:11:19,130 because once something is allocated, 255 00:11:19,130 --> 00:11:21,280 the garbage collector has to get involved. 256 00:11:21,280 --> 00:11:23,050 Again, we gotta learn how to balance 257 00:11:23,050 --> 00:11:25,180 our value and our pointer semantics. 258 00:11:25,180 --> 00:11:27,260 So if none of this is happening 259 00:11:27,260 --> 00:11:31,680 with the code I just showed you, what is happening? 260 00:11:31,680 --> 00:11:34,450 This is one of the most beautiful things about Go. 261 00:11:34,450 --> 00:11:39,210 The Go syntax abstracts away the machine. 262 00:11:39,210 --> 00:11:42,740 It's really powerful, very very interesting. 263 00:11:42,740 --> 00:11:44,450 It abstracts away the machine, 264 00:11:44,450 --> 00:11:48,120 so you don't necessarily have to care where data is, 265 00:11:48,120 --> 00:11:50,300 but when performance starts to matter 266 00:11:50,300 --> 00:11:51,900 and debugging starts to matter, 267 00:11:51,900 --> 00:11:54,020 it does begin to become important 268 00:11:54,020 --> 00:11:57,010 to understand the escape analysis and where things are. 269 00:11:57,010 --> 00:11:59,730 So let's walk through this code with the understanding 270 00:11:59,730 --> 00:12:03,030 of what's really happening thanks to escape analysis. 271 00:12:03,030 --> 00:12:04,683 So, I've got my stack. 272 00:12:05,840 --> 00:12:07,410 I've got main. 273 00:12:07,410 --> 00:12:11,850 We're now in v two, and suddenly what happens 274 00:12:11,850 --> 00:12:14,460 during construction on line 39? 275 00:12:14,460 --> 00:12:15,970 Well the way escape analysis works 276 00:12:15,970 --> 00:12:18,730 is it doesn't care about construction. 277 00:12:18,730 --> 00:12:21,510 Construction in Go tells you nothing. 278 00:12:21,510 --> 00:12:25,530 What tells us everything is how a value is shared. 279 00:12:25,530 --> 00:12:30,440 Sharing tells us everything, and because of line 46 280 00:12:30,440 --> 00:12:34,000 the sharing of the value up the call stack, 281 00:12:34,000 --> 00:12:36,240 that's gonna clue us in that escape analysis 282 00:12:36,240 --> 00:12:41,000 is not gonna construct u user on the stack. 283 00:12:41,000 --> 00:12:43,510 But it's going to go and construct 284 00:12:43,510 --> 00:12:48,043 this user value out on the heap. 285 00:12:49,400 --> 00:12:54,400 Interesting, construction is happening immediately 286 00:12:54,760 --> 00:12:56,400 on the heap. 287 00:12:56,400 --> 00:12:59,820 Now, what gets super interesting to me is this. 288 00:12:59,820 --> 00:13:04,820 U represents a value of type Bill, 289 00:13:05,560 --> 00:13:07,410 but it represents a value of type Bill 290 00:13:07,410 --> 00:13:10,140 that's not on the stack frame, but on the heap. 291 00:13:10,140 --> 00:13:13,620 And what we know is that if you want to access 292 00:13:13,620 --> 00:13:17,610 any sort of data that doesn't exist inside your frame, 293 00:13:17,610 --> 00:13:19,820 remember this is the active frame, 294 00:13:19,820 --> 00:13:22,210 our goroutine is operating here. 295 00:13:22,210 --> 00:13:27,210 If you want to access anything that is not on this frame, 296 00:13:27,220 --> 00:13:30,353 you can only do that through a pointer. 297 00:13:31,700 --> 00:13:34,170 This is really amazing stuff. 298 00:13:34,170 --> 00:13:39,170 From a syntax perspective, u is a value on the heap. 299 00:13:40,070 --> 00:13:42,190 You get to keep your code simple 300 00:13:42,190 --> 00:13:46,860 by manipulating the u value through your value semantics; 301 00:13:46,860 --> 00:13:48,570 however, underneath the covers, 302 00:13:48,570 --> 00:13:52,600 the compiler knows well, I've to have that escape, 303 00:13:52,600 --> 00:13:55,750 so even though the syntax at the coding level 304 00:13:55,750 --> 00:13:59,070 is a value of type u, we'll convert that underneath 305 00:13:59,070 --> 00:14:02,820 to a pointer to be able to access to the heap value. 306 00:14:02,820 --> 00:14:04,360 How cool is that? 307 00:14:04,360 --> 00:14:08,480 The syntax and the language is abstracting the details 308 00:14:08,480 --> 00:14:10,820 and that cognitive load from you, 309 00:14:10,820 --> 00:14:12,360 but it's important that you know that, 310 00:14:12,360 --> 00:14:15,100 but we're really working with u is truly a value 311 00:14:15,100 --> 00:14:16,790 on the heap that you get to work with 312 00:14:16,790 --> 00:14:19,140 as a value and not as a pointer, 313 00:14:19,140 --> 00:14:22,220 which historically you'd have to deal with yourself. 314 00:14:22,220 --> 00:14:24,929 Now when we share u down the callstack, 315 00:14:24,929 --> 00:14:27,570 we're making a copy of that heap address, 316 00:14:27,570 --> 00:14:30,860 and when we get to the return on line 46, 317 00:14:30,860 --> 00:14:35,860 we're making a copy of the heap address, whatever that was. 318 00:14:36,110 --> 00:14:39,710 And so we no longer have an integrity issue. 319 00:14:39,710 --> 00:14:44,613 When we're now back up here as the active frame, 320 00:14:45,610 --> 00:14:48,070 right, and this is our goroutine now, 321 00:14:48,070 --> 00:14:49,210 well, it doesn't matter. 322 00:14:49,210 --> 00:14:51,500 None of this memory, again, is in play. 323 00:14:51,500 --> 00:14:52,950 It's invalid, but it doesn't matter 324 00:14:52,950 --> 00:14:55,610 because we have a pointer to the actual value 325 00:14:55,610 --> 00:14:57,220 that's out there on the heap. 326 00:14:57,220 --> 00:14:59,610 Yay, but there's a cost to this, right? 327 00:14:59,610 --> 00:15:01,530 And that cost was an allocation. 328 00:15:01,530 --> 00:15:04,070 This value now is out on the heap, 329 00:15:04,070 --> 00:15:06,943 and now the garbage collector has to manage it. 330 00:15:08,270 --> 00:15:11,260 So, there's a guideline here that I want to provide 331 00:15:11,260 --> 00:15:14,970 because the ampersand is a very powerful 332 00:15:14,970 --> 00:15:19,970 readability operator, and you don't wanna walk away from it. 333 00:15:20,120 --> 00:15:21,653 Let me ask you this question. 334 00:15:22,570 --> 00:15:25,570 If this is the only code that I showed you, 335 00:15:25,570 --> 00:15:28,880 and I'm zooming into the return here. 336 00:15:28,880 --> 00:15:31,330 If this is the only code that I showed you, 337 00:15:31,330 --> 00:15:35,090 return ampersand u, what do you know already 338 00:15:35,090 --> 00:15:36,910 based on what I've taught you? 339 00:15:36,910 --> 00:15:39,890 You know that there is a cost to this line of code 340 00:15:39,890 --> 00:15:42,280 because even if you don't know what u is, 341 00:15:42,280 --> 00:15:45,247 you now know that it's potentially on the heap. 342 00:15:45,247 --> 00:15:46,120 In fact, it's on the heap. 343 00:15:46,120 --> 00:15:49,250 It's being shared up the call stack, 344 00:15:49,250 --> 00:15:52,720 and therefore, escape analysis is gonna say allocation, 345 00:15:52,720 --> 00:15:54,390 which now means the garbage collector. 346 00:15:54,390 --> 00:15:56,720 You know the cost of this line 347 00:15:56,720 --> 00:16:00,050 because you can read it, ampersand u. 348 00:16:00,050 --> 00:16:01,853 But what if I show you this? 349 00:16:02,690 --> 00:16:05,220 What if I get rid of the ampersand? 350 00:16:05,220 --> 00:16:08,220 Now what does this line of code share with you? 351 00:16:08,220 --> 00:16:09,660 The answer to that question is 352 00:16:09,660 --> 00:16:12,160 it tells you nothing, nothing. 353 00:16:12,160 --> 00:16:15,620 It doesn't necessarily tell you anymore what the cost is. 354 00:16:15,620 --> 00:16:17,990 You have to be able to read the rest of the code. 355 00:16:17,990 --> 00:16:20,880 We just walked away from some readability 356 00:16:20,880 --> 00:16:23,030 because I have to read all of the code 357 00:16:23,030 --> 00:16:24,973 to understand the cost, why? 358 00:16:25,990 --> 00:16:28,460 Because I can do this, and for me, 359 00:16:28,460 --> 00:16:30,820 this is clever code. 360 00:16:30,820 --> 00:16:33,160 This time during the construction, 361 00:16:33,160 --> 00:16:35,280 I am telling the compiler I don't want you 362 00:16:35,280 --> 00:16:37,650 to be a value of type user. 363 00:16:37,650 --> 00:16:39,310 I want it to be a pointer 364 00:16:39,310 --> 00:16:41,670 to the value that we are constructing. 365 00:16:41,670 --> 00:16:44,430 This, to me, is a total nightmare. 366 00:16:44,430 --> 00:16:48,790 We are using pointer semantics during construction, 367 00:16:48,790 --> 00:16:50,950 even though we're creating a variable, 368 00:16:50,950 --> 00:16:53,990 and now we've made this code much harder to read, 369 00:16:53,990 --> 00:16:57,140 and we're really also mixing semantics 370 00:16:57,140 --> 00:16:58,900 as we go along the way. 371 00:16:58,900 --> 00:17:01,690 Anytime you mix semantics we're going to have a problem. 372 00:17:01,690 --> 00:17:04,440 So, here is a general guideline. 373 00:17:04,440 --> 00:17:09,000 I never want to use pointer semantics during construction. 374 00:17:09,000 --> 00:17:12,580 I want to use value semantics during construction. 375 00:17:12,580 --> 00:17:15,660 If you're going to assign that value 376 00:17:15,660 --> 00:17:17,850 that you're constructing to a variable 377 00:17:17,850 --> 00:17:21,710 so we can leverage the highest levels of readability in Go, 378 00:17:21,710 --> 00:17:24,730 we can show sharing down, we can show sharing up, 379 00:17:24,730 --> 00:17:27,100 you can do your own escape analysis. 380 00:17:27,100 --> 00:17:30,830 When we do this, we're walking away from readability. 381 00:17:30,830 --> 00:17:33,900 The only time I want you to use pointer semantics 382 00:17:33,900 --> 00:17:37,373 on construction is if you're gonna do that on a return. 383 00:17:38,260 --> 00:17:40,580 Okay, we're not assigning it to a variable, 384 00:17:40,580 --> 00:17:43,270 we're doing it on a return, or again, 385 00:17:43,270 --> 00:17:45,130 you're gonna do it inside a function call. 386 00:17:45,130 --> 00:17:46,880 We're not assigning it to a variable. 387 00:17:46,880 --> 00:17:49,160 We're doing it within the scope of a function call. 388 00:17:49,160 --> 00:17:52,160 That's where that syntax makes sense. 389 00:17:52,160 --> 00:17:56,930 But if your goal is to assign a value you're constructing 390 00:17:56,930 --> 00:17:59,370 to a variable, please, please 391 00:17:59,370 --> 00:18:02,270 use value semantic construction. 392 00:18:02,270 --> 00:18:04,480 Construction doesn't tell you 393 00:18:04,480 --> 00:18:08,470 where something is in memory, only how you shared. 394 00:18:08,470 --> 00:18:11,810 So if you start the life of a variable as a pointer, 395 00:18:11,810 --> 00:18:14,370 you're walking away from readability, 396 00:18:14,370 --> 00:18:16,260 and you're walking away from this idea 397 00:18:16,260 --> 00:18:18,640 that construction tells you nothing. 398 00:18:18,640 --> 00:18:20,340 This is what we really wanna do. 399 00:18:20,340 --> 00:18:23,020 And you wanna see some really nasty code, 400 00:18:23,020 --> 00:18:25,150 we talk about mixing semantics. 401 00:18:25,150 --> 00:18:27,970 Imagine you saw code like this, 402 00:18:27,970 --> 00:18:29,660 and I've seen this before. 403 00:18:29,660 --> 00:18:32,320 This stuff really makes me cry. 404 00:18:32,320 --> 00:18:35,150 Look, we're using value semantics on the return, 405 00:18:35,150 --> 00:18:37,907 pointer semantics on line 39 on the construction, 406 00:18:37,907 --> 00:18:41,030 and you're going back to value semantics on line 46 407 00:18:41,030 --> 00:18:42,510 because you gotta get back to it. 408 00:18:42,510 --> 00:18:45,090 And what's really interesting about this code is 409 00:18:45,090 --> 00:18:47,300 that value never escapes the heap. 410 00:18:47,300 --> 00:18:48,990 We're using value semantics, 411 00:18:48,990 --> 00:18:51,887 yet your code is using pointers. 412 00:18:51,887 --> 00:18:54,830 Oh, I wanna just duct tape my head 413 00:18:54,830 --> 00:18:56,420 when I see this kind of stuff. 414 00:18:56,420 --> 00:18:58,200 This is not good. 415 00:18:58,200 --> 00:19:00,670 So, we want to make sure that we're using 416 00:19:00,670 --> 00:19:04,080 the right semantics and semantic consistency 417 00:19:04,080 --> 00:19:05,800 all of the time. 418 00:19:05,800 --> 00:19:08,320 We don't want to construct values 419 00:19:08,320 --> 00:19:10,270 to variables using pointer semantics. 420 00:19:10,270 --> 00:19:12,710 We want to leverage the ampersand in the right place. 421 00:19:12,710 --> 00:19:16,100 Now what's also very powerful here is 422 00:19:16,100 --> 00:19:19,550 you're gonna use go build to build your binaries, 423 00:19:19,550 --> 00:19:23,150 and when you use the gc flags on the go build calls, 424 00:19:23,150 --> 00:19:27,690 and we'll do this as well when we're on go testing later on, 425 00:19:27,690 --> 00:19:32,690 but when you use this set of flags on building and testing, 426 00:19:33,970 --> 00:19:36,010 what you're gonna get is not a binary, 427 00:19:36,010 --> 00:19:39,150 but what we're gonna get is the escape analysis report. 428 00:19:39,150 --> 00:19:41,390 We're gonna come back to this during profiling 429 00:19:41,390 --> 00:19:44,110 because I want to show you how powerful it is 430 00:19:44,110 --> 00:19:46,890 to have this report because when we look at a profiler, 431 00:19:46,890 --> 00:19:48,580 it can only show us what is allocating. 432 00:19:48,580 --> 00:19:49,910 It can't tell us why. 433 00:19:49,910 --> 00:19:52,690 This report tells us why something is allocating. 434 00:19:52,690 --> 00:19:54,360 So I don't want you using it while you're coding. 435 00:19:54,360 --> 00:19:56,993 We're gonna use this as it relates to profiling. 436 00:19:57,910 --> 00:20:01,520 But notice down here, what it basically tells us is 437 00:20:01,520 --> 00:20:05,350 that we're u to the heap and it's because of the return. 438 00:20:05,350 --> 00:20:08,500 Again, the return is causing a share up the call stack, 439 00:20:08,500 --> 00:20:10,830 which means you can't leave it on this frame 440 00:20:10,830 --> 00:20:12,080 'cause once we come back here, 441 00:20:12,080 --> 00:20:16,260 this is the active frame, it has to escape to the heap. 442 00:20:16,260 --> 00:20:17,630 We now have an allocating, 443 00:20:17,630 --> 00:20:19,630 we now need garbage collection involved.