1 00:00:06,700 --> 00:00:08,860 - Okay, next we've gotta learn about struct types, 2 00:00:08,860 --> 00:00:11,070 or really, user defined types, 3 00:00:11,070 --> 00:00:13,270 the language would be useless if you couldn't define 4 00:00:13,270 --> 00:00:15,050 your own data definitions, right? 5 00:00:15,050 --> 00:00:15,883 Your data types. 6 00:00:15,883 --> 00:00:17,870 And Go, again, is not being novel here. 7 00:00:17,870 --> 00:00:19,750 Lots of languages do that, 8 00:00:19,750 --> 00:00:21,250 so there is some important things, here, 9 00:00:21,250 --> 00:00:22,083 I want to share with you. 10 00:00:22,083 --> 00:00:24,300 Not necessarily teach you, what a user defined type is, 11 00:00:24,300 --> 00:00:27,420 but, kinda, how they exist in Go. 12 00:00:27,420 --> 00:00:29,580 And there's a great quote here by Martin Thompson, 13 00:00:29,580 --> 00:00:31,760 who really coined the phrase, mechanical sympathy. 14 00:00:31,760 --> 00:00:33,237 Look him up if you don't know him. 15 00:00:33,237 --> 00:00:36,067 "Implicit conversion of types is a Halloween 16 00:00:36,067 --> 00:00:37,777 "special of coding. 17 00:00:37,777 --> 00:00:40,690 "Whoever thought of them deserves their own special hell." 18 00:00:40,690 --> 00:00:43,490 We'll come back to this implicit conversion, here. 19 00:00:43,490 --> 00:00:44,550 And Martin Thompson, 20 00:00:44,550 --> 00:00:47,100 I just, I really love that quote. 21 00:00:47,100 --> 00:00:50,000 All right, let's do a little code review here. 22 00:00:50,000 --> 00:00:52,070 And let's start with some basics here 23 00:00:52,070 --> 00:00:54,710 around our user defined types. 24 00:00:54,710 --> 00:00:55,780 So, there it is in Go. 25 00:00:55,780 --> 00:01:00,000 In line 10 of defining a named type called example. 26 00:01:00,000 --> 00:01:02,100 It's a struct type, a composite type. 27 00:01:02,100 --> 00:01:04,250 It's made up of three other existing types. 28 00:01:04,250 --> 00:01:08,280 Bool, int16, and float32, there it is. 29 00:01:08,280 --> 00:01:09,890 Nothing really special going on. 30 00:01:09,890 --> 00:01:13,310 And you see on line 20 that I'm creating a value 31 00:01:13,310 --> 00:01:18,310 of type example, named e1, setting it to its zero value. 32 00:01:18,390 --> 00:01:20,540 You won't hear me use the word object in Go. 33 00:01:20,540 --> 00:01:22,510 We create values in Go. 34 00:01:22,510 --> 00:01:23,870 You'll continue to hear me say that. 35 00:01:23,870 --> 00:01:26,590 Listen to the language and the words I'm using. 36 00:01:26,590 --> 00:01:28,230 I'm going to be very consistent with them 37 00:01:28,230 --> 00:01:30,340 and it's going to help us throughout the class. 38 00:01:30,340 --> 00:01:31,620 So, again, on line 20, 39 00:01:31,620 --> 00:01:36,620 we are constructing or creating a value of type example 40 00:01:37,280 --> 00:01:41,710 named e1 set to its zero value. 41 00:01:41,710 --> 00:01:46,070 But let's go back here and talk about the type declaration 42 00:01:46,070 --> 00:01:50,820 because I want to focus a lot if performance matters 43 00:01:50,820 --> 00:01:51,780 and these things matter, 44 00:01:51,780 --> 00:01:53,990 with the idea that we optimize for correctness. 45 00:01:53,990 --> 00:01:57,700 First, and at the same time, there's some interesting 46 00:01:57,700 --> 00:01:59,980 things about structs that I want to share. 47 00:01:59,980 --> 00:02:02,560 And if we're really going to understand cost, 48 00:02:02,560 --> 00:02:04,860 I told you we have to understand allocations. 49 00:02:04,860 --> 00:02:07,830 And so then, the first question I'm going to ask you is, 50 00:02:07,830 --> 00:02:09,840 how much memory is allocated 51 00:02:09,840 --> 00:02:12,340 for the e1 variable on line 20? 52 00:02:12,340 --> 00:02:15,840 I mean, any time we construct a value, of type example, 53 00:02:15,840 --> 00:02:18,470 how much memory has to be allocated? 54 00:02:18,470 --> 00:02:20,087 Now, you're first guess might be, 55 00:02:20,087 --> 00:02:22,550 "Well, Bill, it's a composite type." 56 00:02:22,550 --> 00:02:25,450 So, you know I could just do the following. 57 00:02:25,450 --> 00:02:28,877 I could say, "Okay, the first field is a flag, 58 00:02:28,877 --> 00:02:31,377 "so, Bill, you know, that's a boolean, right? 59 00:02:31,377 --> 00:02:34,817 "And that boolean would just then be one byte. 60 00:02:34,817 --> 00:02:38,727 "And then, Bill, I've got a counter, which is int16. 61 00:02:38,727 --> 00:02:41,917 "So, my counter, Bill, that's an int16, 62 00:02:41,917 --> 00:02:43,910 "so that's two bytes." 63 00:02:43,910 --> 00:02:45,607 Draw a little line for the two bytes. 64 00:02:45,607 --> 00:02:48,207 "And then, Bill, you know we've got pi here. 65 00:02:48,207 --> 00:02:51,067 "And that's a 4-byte value, Bill, you know. 66 00:02:51,067 --> 00:02:54,880 "And what was that, a float32, Bill." 67 00:02:54,880 --> 00:02:57,910 You know, if I add these three things up, 68 00:02:57,910 --> 00:02:59,897 I'm gonna get a total of seven bytes. 69 00:02:59,897 --> 00:03:03,017 "So, Bill, it seems to me that every time I construct 70 00:03:03,017 --> 00:03:05,880 "a value of type example, it's going to be seven." 71 00:03:05,880 --> 00:03:08,093 Now, I don't think it's a bad guess, 72 00:03:09,138 --> 00:03:09,971 it's very realistic. 73 00:03:09,971 --> 00:03:12,340 The problem is, is that that's going to be wrong. 74 00:03:12,340 --> 00:03:14,430 And the reason that's going to be wrong is, 75 00:03:14,430 --> 00:03:17,600 'cause we have the concept of alignments. 76 00:03:17,600 --> 00:03:19,070 And they come from the hardware. 77 00:03:19,070 --> 00:03:21,860 And alignments are there to make reading and writing 78 00:03:21,860 --> 00:03:25,720 memory as efficient as possible. 79 00:03:25,720 --> 00:03:28,070 As we move across the hardware in this class, 80 00:03:28,070 --> 00:03:30,200 we're going to see that there are these different 81 00:03:30,200 --> 00:03:32,420 boundaries, memory boundaries, 82 00:03:32,420 --> 00:03:34,550 that the hardware is dealing with. 83 00:03:34,550 --> 00:03:36,680 And one of these boundaries is, 84 00:03:36,680 --> 00:03:38,410 you know the hardware's ability to pull data 85 00:03:38,410 --> 00:03:41,090 in and out, into the hardware. 86 00:03:41,090 --> 00:03:43,660 And so, what we end up with are these machine 87 00:03:43,660 --> 00:03:44,810 word boundaries. 88 00:03:44,810 --> 00:03:47,460 And I just want you to get a general sense 89 00:03:47,460 --> 00:03:50,350 of what's happening here, with alignments, 90 00:03:50,350 --> 00:03:52,080 and why they're in place. 91 00:03:52,080 --> 00:03:55,341 And then, why this really isn't a 7-byte value, 92 00:03:55,341 --> 00:03:58,220 but we're going to find out that it's an 8-byte value. 93 00:03:58,220 --> 00:04:02,340 So if we take memory on these word boundaries. 94 00:04:02,340 --> 00:04:04,070 Here we go, in these word boundaries. 95 00:04:04,070 --> 00:04:07,650 Then the idea is this, the hardware has the ability 96 00:04:07,650 --> 00:04:11,040 let's say within one instruction, to read and write 97 00:04:11,040 --> 00:04:13,419 this word boundary. 98 00:04:13,419 --> 00:04:15,440 And then, read and write this word boundary. 99 00:04:15,440 --> 00:04:19,050 So the idea is that if we would allow a 2-byte value, 100 00:04:19,050 --> 00:04:20,577 let's say we had a 2-byte value, 101 00:04:20,577 --> 00:04:24,670 and we allowed that 2-byte value to be placed in memory, 102 00:04:24,670 --> 00:04:28,010 across this boundary like this. 103 00:04:28,010 --> 00:04:29,220 Let's say that's our value. 104 00:04:29,220 --> 00:04:32,200 We've just now allowed it to align in memory, 105 00:04:32,200 --> 00:04:36,590 in such a way that it crosses over these two boundaries. 106 00:04:36,590 --> 00:04:38,710 Then it means it would take the hardware 107 00:04:38,710 --> 00:04:42,710 one and two operations to read it in, 108 00:04:42,710 --> 00:04:46,460 and then, one, and two operations to write it out. 109 00:04:46,460 --> 00:04:48,970 I mean, not efficient, and really sets us up 110 00:04:48,970 --> 00:04:51,230 for other nasty things later on. 111 00:04:51,230 --> 00:04:52,390 We don't want to do that. 112 00:04:52,390 --> 00:04:53,417 The hardware doesn't want to do that, 113 00:04:53,417 --> 00:04:54,870 and we don't want to do that. 114 00:04:54,870 --> 00:04:56,653 So, the idea around alignments is, 115 00:04:56,653 --> 00:04:59,180 there's no reason to allow this value 116 00:04:59,180 --> 00:05:01,870 to cross over these word boundaries here 117 00:05:01,870 --> 00:05:06,080 when it can easily fit inside of a single word boundary. 118 00:05:06,080 --> 00:05:09,110 So, the idea of alignments, is to say, 119 00:05:09,110 --> 00:05:14,110 let's make sure that all values, wherever they are, 120 00:05:14,320 --> 00:05:17,210 fall within these word boundaries. 121 00:05:17,210 --> 00:05:19,130 So if I have a 2-byte value, 122 00:05:19,130 --> 00:05:22,300 then they always fall inside the boundary. 123 00:05:22,300 --> 00:05:24,800 Now, what that means is, is that we're going 124 00:05:24,800 --> 00:05:28,730 to end up with this other concept of padding inside 125 00:05:28,730 --> 00:05:29,830 of our structs. 126 00:05:29,830 --> 00:05:32,350 So let's go back to our struct, for a second, 127 00:05:32,350 --> 00:05:35,743 and start to understand what alignments and padding do. 128 00:05:36,690 --> 00:05:40,380 So, the way this is going to work, is the following. 129 00:05:40,380 --> 00:05:43,480 If you have a 1-byte value, like a bool, 130 00:05:44,460 --> 00:05:46,480 well that will fit inside of a word boundary 131 00:05:46,480 --> 00:05:47,970 no matter where you put it. 132 00:05:47,970 --> 00:05:51,340 It could never cross over, because byte is our basic 133 00:05:51,340 --> 00:05:54,020 unit of memory, read and write, that will fit inside 134 00:05:54,020 --> 00:05:55,770 of any word boundary cleanly. 135 00:05:55,770 --> 00:05:58,960 Well, when you get to a 2-byte value, like counter, 136 00:05:58,960 --> 00:06:00,280 now as I showed before, 137 00:06:00,280 --> 00:06:02,400 we could cross over these boundaries. 138 00:06:02,400 --> 00:06:04,880 So what we need to do is, make sure that any 2-byte value 139 00:06:04,880 --> 00:06:07,710 always aligns within a single word boundary. 140 00:06:07,710 --> 00:06:08,690 How do you do that? 141 00:06:08,690 --> 00:06:10,810 Well, you make sure that that value always falls 142 00:06:10,810 --> 00:06:13,030 within a 2-byte address scheme. 143 00:06:13,030 --> 00:06:15,950 In other words, if we look at the last digit of any address, 144 00:06:15,950 --> 00:06:19,750 it always falls on a multiple of two, 145 00:06:19,750 --> 00:06:20,960 in terms of its address. 146 00:06:20,960 --> 00:06:24,090 It falls within, so address zero, address two, 147 00:06:24,090 --> 00:06:26,190 address four, address six, address eight, 148 00:06:26,190 --> 00:06:27,023 you get it. 149 00:06:27,023 --> 00:06:28,600 And if we have a 4-byte value, 150 00:06:28,600 --> 00:06:31,180 then it has to fall on a 4-byte alignment. 151 00:06:31,180 --> 00:06:33,070 Address zero, address four, address eight, 152 00:06:33,070 --> 00:06:33,903 you get it? 153 00:06:33,903 --> 00:06:35,380 And if it's an 8-byte value, 154 00:06:35,380 --> 00:06:39,690 like arrange64, then it's gotta fall in that full boundary 155 00:06:39,690 --> 00:06:41,350 address zero, address eight. 156 00:06:41,350 --> 00:06:44,290 So, what we're doing, is looking at the size of a value 157 00:06:44,290 --> 00:06:46,640 to determine what its alignment is, 158 00:06:46,640 --> 00:06:48,580 and then making sure that it falls properly 159 00:06:48,580 --> 00:06:50,820 in memory within those boundaries. 160 00:06:50,820 --> 00:06:54,113 So, if I go back and we look at our example, 161 00:06:54,990 --> 00:06:56,770 let me draw that back up. 162 00:06:56,770 --> 00:06:58,450 What we originally said was, 163 00:06:58,450 --> 00:07:01,160 okay, here's our flag, right? 164 00:07:01,160 --> 00:07:02,620 That's our one byte. 165 00:07:02,620 --> 00:07:04,650 And then here was that counter, 166 00:07:04,650 --> 00:07:06,180 and that was two bytes. 167 00:07:06,180 --> 00:07:08,020 And then here was pi. 168 00:07:08,020 --> 00:07:08,853 Right? 169 00:07:08,853 --> 00:07:10,700 And that was the four bytes. 170 00:07:10,700 --> 00:07:13,600 But we said this really isn't a seven byte. 171 00:07:13,600 --> 00:07:16,060 One, two, three, four, five, six, seven. 172 00:07:16,060 --> 00:07:17,430 It's really eight. 173 00:07:17,430 --> 00:07:20,790 So let's start mapping this out by address. 174 00:07:20,790 --> 00:07:24,640 If the flag byte starts at address zero, 175 00:07:24,640 --> 00:07:26,940 which is quite possible, 176 00:07:26,940 --> 00:07:28,890 then let's go back to counter, 177 00:07:28,890 --> 00:07:31,790 counter is a 2-byte value. 178 00:07:31,790 --> 00:07:34,940 Which means it has to fall on a two byte alignment. 179 00:07:34,940 --> 00:07:38,040 Which means its address has to be within a two byte. 180 00:07:38,040 --> 00:07:40,270 So, zero, two, four, six, eight. 181 00:07:40,270 --> 00:07:44,340 Which means that counter must start on address two. 182 00:07:44,340 --> 00:07:46,550 If it's going to be properly aligned. 183 00:07:46,550 --> 00:07:49,370 It has to always fall within a multiple of two. 184 00:07:49,370 --> 00:07:52,920 Well, that's interesting because if flag is one byte, 185 00:07:52,920 --> 00:07:55,380 and falls on address zero, 186 00:07:55,380 --> 00:07:58,963 what would happen to the byte at address one? 187 00:07:59,810 --> 00:08:01,140 That's our padding. 188 00:08:01,140 --> 00:08:04,770 This byte exists and it, unfortunately, 189 00:08:04,770 --> 00:08:06,320 has to be skipped over. 190 00:08:06,320 --> 00:08:08,430 It's part of the value itself, 191 00:08:08,430 --> 00:08:09,830 but it's not used. 192 00:08:09,830 --> 00:08:12,230 The fact that we've got bool int16, 193 00:08:12,230 --> 00:08:13,940 the fact that the alignment of int16 194 00:08:13,940 --> 00:08:15,430 has to be on address two, 195 00:08:15,430 --> 00:08:17,610 means that this byte here, at address one, 196 00:08:17,610 --> 00:08:20,060 gets skipped over, this becomes padding. 197 00:08:20,060 --> 00:08:22,750 Now you can see where you've got one, two, three, 198 00:08:22,750 --> 00:08:25,040 four, five, six, seven, eight. 199 00:08:25,040 --> 00:08:25,873 There it is, 200 00:08:25,873 --> 00:08:27,000 it's an 8-byte value. 201 00:08:27,000 --> 00:08:29,600 All right, now, there's a couple things here before 202 00:08:29,600 --> 00:08:32,500 we make this a little bit more complex, okay? 203 00:08:32,500 --> 00:08:37,160 I always want to focus on optimizing for correctness. 204 00:08:37,160 --> 00:08:39,910 Which means, I don't care about padding right now, 205 00:08:39,910 --> 00:08:41,990 unless it is a problem. 206 00:08:41,990 --> 00:08:44,600 The only way padding could be a problem initially, 207 00:08:44,600 --> 00:08:47,420 is maybe we've got so much padding in the struct 208 00:08:47,420 --> 00:08:50,880 that it's allocating much more memory than we want it to. 209 00:08:50,880 --> 00:08:53,920 And therefore, maybe we've got a larger footprint in memory. 210 00:08:53,920 --> 00:08:56,540 But now we're talking about micro-optimizations based 211 00:08:56,540 --> 00:08:58,100 on a memory profile. 212 00:08:58,100 --> 00:08:59,620 How is that possible? 213 00:08:59,620 --> 00:09:01,370 Well, think abut this for a second. 214 00:09:02,450 --> 00:09:04,240 What if I did this instead? 215 00:09:04,240 --> 00:09:06,950 Not int16 but int32. 216 00:09:06,950 --> 00:09:08,400 Now what do we have? 217 00:09:08,400 --> 00:09:11,050 Now what we have is not one byte of padding, 218 00:09:11,050 --> 00:09:14,580 here, but now we have three bytes of padding, here. 219 00:09:14,580 --> 00:09:18,630 Because this now has to start on address four. 220 00:09:18,630 --> 00:09:22,000 int32, 4-byte value, 4-byte alignment. 221 00:09:22,000 --> 00:09:23,970 Now, you got three bytes of padding here. 222 00:09:23,970 --> 00:09:26,390 Hey, what if this is int64? 223 00:09:26,390 --> 00:09:29,300 Well, if that's int64, then what? 224 00:09:29,300 --> 00:09:31,810 Now this has to start, not on four, 225 00:09:31,810 --> 00:09:33,920 this has to start on eight, 226 00:09:33,920 --> 00:09:37,810 which now means we've got seven bytes of padding, here. 227 00:09:37,810 --> 00:09:40,010 You can see how something like this 228 00:09:40,010 --> 00:09:42,590 could add up very, very quickly. 229 00:09:42,590 --> 00:09:45,220 I mean, again, we're talking about micro-optimizations 230 00:09:45,220 --> 00:09:47,310 and nothing we would do during coding, 231 00:09:47,310 --> 00:09:52,120 but imagine we had a struct where size was important. 232 00:09:52,120 --> 00:09:54,410 And though the field names would have to be different, 233 00:09:54,410 --> 00:09:56,150 we did something like this. 234 00:09:56,150 --> 00:09:58,970 Now you've got seven bytes of padding, 235 00:09:58,970 --> 00:10:00,530 and seven bytes of padding, 236 00:10:00,530 --> 00:10:03,070 and seven, we've got an extra 21 bytes of padding, 237 00:10:03,070 --> 00:10:05,290 or memory, for a value of type example 238 00:10:05,290 --> 00:10:07,140 that we really don't necessarily need. 239 00:10:07,140 --> 00:10:09,550 Again, I want to optimize for correctness, 240 00:10:09,550 --> 00:10:13,420 so if this is a better way to look and read the struct, 241 00:10:13,420 --> 00:10:17,280 I'll take the 21 bytes until a memory profile 242 00:10:17,280 --> 00:10:19,410 is telling us we shouldn't. 243 00:10:19,410 --> 00:10:23,800 Now, if we truly want to micro-optimize the padding away, 244 00:10:23,800 --> 00:10:25,270 what we need to do is, 245 00:10:25,270 --> 00:10:29,430 order fields from largest to smallest. 246 00:10:29,430 --> 00:10:32,510 When you order fields from largest to smallest, 247 00:10:32,510 --> 00:10:33,637 what that will do, 248 00:10:33,637 --> 00:10:35,020 I'm just playing around here, 249 00:10:35,020 --> 00:10:37,583 just to give you that, kind of, visual clue. 250 00:10:39,060 --> 00:10:41,900 What that will do is, is reduce padding all the way down 251 00:10:41,900 --> 00:10:44,760 if there needs to be any, to the bottom of the struct. 252 00:10:44,760 --> 00:10:46,270 Because alignments are always 253 00:10:46,270 --> 00:10:49,630 going to find evenness throughout the struct. 254 00:10:49,630 --> 00:10:53,710 Because there's another part of this alignment equation. 255 00:10:53,710 --> 00:10:57,140 And that is a struct must also properly align 256 00:10:57,140 --> 00:10:59,030 based on its largest field. 257 00:10:59,030 --> 00:11:00,310 So, this struct right now, 258 00:11:00,310 --> 00:11:02,460 its largest field is an 8-byte value. 259 00:11:02,460 --> 00:11:04,380 So there's also an 8-byte alignment 260 00:11:04,380 --> 00:11:05,915 that has to happen on the struct. 261 00:11:05,915 --> 00:11:07,720 There's going to be padding at the bottom 262 00:11:07,720 --> 00:11:09,450 of this structure, right here. 263 00:11:09,450 --> 00:11:12,600 If I got rid of int64 and there was just int32, 264 00:11:12,600 --> 00:11:15,830 now the entire struct has to fall on a multiple of four. 265 00:11:15,830 --> 00:11:17,227 Now it has to be a 4-byte padding. 266 00:11:17,227 --> 00:11:19,290 You see, you've got that stuff too. 267 00:11:19,290 --> 00:11:21,500 But I promise you that if I see a struct 268 00:11:21,500 --> 00:11:23,550 that's laid out where the fields, 269 00:11:23,550 --> 00:11:26,380 from its largest size to its smallest size, 270 00:11:26,380 --> 00:11:28,640 that's going to raise a flag for me. 271 00:11:28,640 --> 00:11:29,530 I'm going to ask you, 272 00:11:29,530 --> 00:11:34,220 why are you pre-optimizing this struct, right now? 273 00:11:34,220 --> 00:11:35,660 Do you have a memory profile? 274 00:11:35,660 --> 00:11:37,560 Do you have something that's telling you 275 00:11:37,560 --> 00:11:39,440 that we're using up too much memory? 276 00:11:39,440 --> 00:11:42,100 If the answer's no, I'm going to ask you to reorder 277 00:11:42,100 --> 00:11:44,940 the fields based on 278 00:11:44,940 --> 00:11:45,820 correctness. 279 00:11:45,820 --> 00:11:47,080 What is a better way to read it? 280 00:11:47,080 --> 00:11:50,550 Let's group things together that belong together. 281 00:11:50,550 --> 00:11:53,010 Instead of going right into this idea 282 00:11:53,010 --> 00:11:54,820 of optimizing for performance. 283 00:11:54,820 --> 00:11:57,477 And you might say, "Well, Bill, why doesn't the language 284 00:11:57,477 --> 00:11:58,490 "do this for you?" 285 00:11:58,490 --> 00:12:00,600 Because the language isn't going to go behind your back 286 00:12:00,600 --> 00:12:01,433 and make changes, 287 00:12:01,433 --> 00:12:03,430 because then we would lose our ability to understand 288 00:12:03,430 --> 00:12:05,170 the impact that we're having. 289 00:12:05,170 --> 00:12:07,850 You get to control your memory layouts. 290 00:12:07,850 --> 00:12:10,820 You get to make them as precise as you need them to be. 291 00:12:10,820 --> 00:12:12,570 And it's not the language's job to go behind 292 00:12:12,570 --> 00:12:15,620 the back and try to pre-optimize things for you, either. 293 00:12:15,620 --> 00:12:17,530 There are some languages that might do that, 294 00:12:17,530 --> 00:12:19,360 Go isn't going to do that. 295 00:12:19,360 --> 00:12:22,010 So, the idea again, around 296 00:12:22,010 --> 00:12:24,430 understanding the size of a struct, 297 00:12:24,430 --> 00:12:27,510 means we have to understand alignments and padding. 298 00:12:27,510 --> 00:12:30,360 And now we know that, based on the size of a field, 299 00:12:30,360 --> 00:12:31,900 and this would be anywhere in memory. 300 00:12:31,900 --> 00:12:34,430 You don't really feel it until we get to the struct types. 301 00:12:34,430 --> 00:12:37,220 But our int16s have to fall on a 2-byte alignment. 302 00:12:37,220 --> 00:12:39,800 Our float32 has to fall on a 4-byte alignment. 303 00:12:39,800 --> 00:12:41,580 Our bools can kind of be put anywhere, 304 00:12:41,580 --> 00:12:44,100 and that can cause places for padding. 305 00:12:44,100 --> 00:12:46,490 So, we really got an 8-byte value, here. 306 00:12:46,490 --> 00:12:49,310 So on line 20 when we construct our e1 variable 307 00:12:49,310 --> 00:12:52,210 or value of type example, 308 00:12:52,210 --> 00:12:54,000 that's eight bytes of memory cost. 309 00:12:54,000 --> 00:12:55,600 Now we know that is. 310 00:12:55,600 --> 00:12:57,643 And I wanna show you on line 27, 311 00:12:58,570 --> 00:13:00,490 what line 27 is showing you, 312 00:13:00,490 --> 00:13:05,490 is how we do construction when we don't want zero value. 313 00:13:05,520 --> 00:13:08,540 And this is going to be a very common syntax in Go. 314 00:13:08,540 --> 00:13:11,800 We're going to call this a literal construction. 315 00:13:11,800 --> 00:13:14,260 So over here, you see literal construction 316 00:13:14,260 --> 00:13:15,960 of the name type example. 317 00:13:15,960 --> 00:13:17,330 We're initializing field 318 00:13:17,330 --> 00:13:18,300 counter in pi. 319 00:13:18,300 --> 00:13:20,280 You see the syntax with the colon. 320 00:13:20,280 --> 00:13:22,350 You also see a comma at the end of every line, 321 00:13:22,350 --> 00:13:25,260 Go is very much about convention over configuration. 322 00:13:25,260 --> 00:13:26,790 It's very opinionated here. 323 00:13:26,790 --> 00:13:30,100 Go funct is going to play a big role in your life here, 324 00:13:30,100 --> 00:13:32,410 and so, these are opinions that you have. 325 00:13:32,410 --> 00:13:33,600 Whether you like it or not, 326 00:13:33,600 --> 00:13:35,130 this is the way it's going to be. 327 00:13:35,130 --> 00:13:36,630 You can see, the use of the short variable 328 00:13:36,630 --> 00:13:40,350 declaration operator during this construction, as well. 329 00:13:40,350 --> 00:13:42,110 And Go is not being novel, 330 00:13:42,110 --> 00:13:43,550 we're going to use the dot operator, 331 00:13:43,550 --> 00:13:46,410 value dot field, value dot field. 332 00:13:46,410 --> 00:13:49,830 Now, you might see this in a lot of code in Go. 333 00:13:49,830 --> 00:13:53,100 You might see the use of literal construction, 334 00:13:53,100 --> 00:13:56,190 to create a value set to its zero value. 335 00:13:56,190 --> 00:13:58,330 There is nothing wrong with this. 336 00:13:58,330 --> 00:13:59,310 I don't do it again. 337 00:13:59,310 --> 00:14:04,310 Because I'd rather use var to show zero value construction, 338 00:14:05,240 --> 00:14:07,830 like I showed you there, on line 20. 339 00:14:07,830 --> 00:14:10,550 Line 27 is also doing zero value construction, 340 00:14:10,550 --> 00:14:11,383 in this case. 341 00:14:11,383 --> 00:14:13,750 But what I tried to tell you before is, 342 00:14:13,750 --> 00:14:17,560 just because literal construction here, 343 00:14:17,560 --> 00:14:19,530 literal empty construction here, 344 00:14:19,530 --> 00:14:21,270 as struct is creating zero value, 345 00:14:21,270 --> 00:14:23,750 that's not necessarily the case all the time. 346 00:14:23,750 --> 00:14:25,020 So I'd rather use var. 347 00:14:25,020 --> 00:14:27,240 But there's nothing wrong with that, 348 00:14:27,240 --> 00:14:28,600 if that's what you want to do. 349 00:14:28,600 --> 00:14:30,090 Just be consistent, 350 00:14:30,090 --> 00:14:32,090 but in this code that we're going to go through, 351 00:14:32,090 --> 00:14:34,960 we're going to use a literal construction 352 00:14:34,960 --> 00:14:38,370 most of the time when we want initialize 353 00:14:38,370 --> 00:14:41,060 to something other than zero value, 'kay? 354 00:14:41,060 --> 00:14:44,340 The only time you might see empty literal construction 355 00:14:44,340 --> 00:14:47,760 from me, is when we're not assigning it to a variable. 356 00:14:47,760 --> 00:14:49,790 You might see it on a return. 357 00:14:49,790 --> 00:14:51,680 That will be absolutely fine. 358 00:14:51,680 --> 00:14:54,060 You might see it on a function call. 359 00:14:54,060 --> 00:14:58,000 That will be the exception to empty literal construction. 360 00:14:58,000 --> 00:15:00,720 But anytime we're assigning it to a variable, 361 00:15:00,720 --> 00:15:02,860 I'm not going to do that at all. 362 00:15:02,860 --> 00:15:03,710 Okay. 363 00:15:03,710 --> 00:15:07,080 So, that's our basic, kind of, struct or layout, 364 00:15:07,080 --> 00:15:08,640 or user defined struct. 365 00:15:08,640 --> 00:15:10,450 But let's take a look at something 366 00:15:10,450 --> 00:15:13,203 that I think is a little bit more interesting. 367 00:15:14,280 --> 00:15:18,190 Go also has the concept of an anonymous struct. 368 00:15:18,190 --> 00:15:20,690 We'll call these literals in the language. 369 00:15:20,690 --> 00:15:23,420 Any time you see a compiler message about a literal value, 370 00:15:23,420 --> 00:15:24,510 or literal type, 371 00:15:24,510 --> 00:15:26,980 what we're really talking about is an unnamed type. 372 00:15:26,980 --> 00:15:29,470 So, there are things in this language that are named 373 00:15:29,470 --> 00:15:31,080 and there are things that are unnamed. 374 00:15:31,080 --> 00:15:32,800 What you see on line 14, 375 00:15:32,800 --> 00:15:35,880 is us declaring a variable name e1, 376 00:15:35,880 --> 00:15:38,160 based on the literal struct type. 377 00:15:38,160 --> 00:15:40,650 I can't give it a name because there isn't any. 378 00:15:40,650 --> 00:15:43,810 We're using var so again, it's set to its zero value. 379 00:15:43,810 --> 00:15:46,490 You know, readability is about consistency. 380 00:15:46,490 --> 00:15:49,240 And you can start seeing our leveraging var 381 00:15:49,240 --> 00:15:52,470 to have a very consistent look and feel around zero value. 382 00:15:52,470 --> 00:15:53,880 And you can see on line 21, 383 00:15:53,880 --> 00:15:56,130 I'm displaying the value of e1, 384 00:15:56,130 --> 00:15:58,390 which will all be set to its zero value. 385 00:15:58,390 --> 00:15:59,900 And here, what you're seeing, 386 00:15:59,900 --> 00:16:03,400 is I'm doing literal construction. 387 00:16:03,400 --> 00:16:06,190 Actually this is, again, type declaration, 388 00:16:06,190 --> 00:16:09,480 so we're declaring on the fly, a struct. 389 00:16:09,480 --> 00:16:11,710 It's a literal struct and has no name. 390 00:16:11,710 --> 00:16:15,300 And right below it, you now see the literal construction, 391 00:16:15,300 --> 00:16:17,700 'cause we're not going to set it to zero value. 392 00:16:17,700 --> 00:16:19,340 You'll see a lot of this in Go. 393 00:16:19,340 --> 00:16:22,050 These literal types can come in really handy 394 00:16:22,050 --> 00:16:23,690 for things like, when you're doing, 395 00:16:23,690 --> 00:16:25,960 let's say unmarsheling on a web API. 396 00:16:25,960 --> 00:16:27,950 A piece adjacent might come in, 397 00:16:27,950 --> 00:16:29,230 you have to do some unmarsheling, 398 00:16:29,230 --> 00:16:30,830 you need some type information, 399 00:16:30,830 --> 00:16:33,010 but it's not necessarily good to name it. 400 00:16:33,010 --> 00:16:35,440 Because this only need to be used in this one place. 401 00:16:35,440 --> 00:16:36,680 And we don't want to name something 402 00:16:36,680 --> 00:16:37,890 we're only using in one place. 403 00:16:37,890 --> 00:16:39,160 That would be pollution. 404 00:16:39,160 --> 00:16:41,890 We get the ability to find these structs literally 405 00:16:41,890 --> 00:16:44,260 where they're needed and use them right then and there. 406 00:16:44,260 --> 00:16:48,310 It also really enhances readability since this is struct, 407 00:16:48,310 --> 00:16:50,880 it is leveraged the same way. 408 00:16:50,880 --> 00:16:51,713 Okay. 409 00:16:51,713 --> 00:16:54,309 Let's focus on this e2 struct here, 410 00:16:54,309 --> 00:16:55,641 or the e2 variable, 411 00:16:55,641 --> 00:16:58,180 because I want to talk about this, as well. 412 00:16:58,180 --> 00:17:01,080 Let me take this struct declaration that we have. 413 00:17:01,080 --> 00:17:03,460 And let's do something very interesting. 414 00:17:03,460 --> 00:17:06,740 Let's go ahead and define two types. 415 00:17:06,740 --> 00:17:08,200 I'm going to have type bill 416 00:17:09,220 --> 00:17:12,630 and let's create a type named alice. 417 00:17:12,630 --> 00:17:16,620 Now, I'm going to hit the format buttons so we can do this. 418 00:17:16,620 --> 00:17:19,700 I want you to notice something about bill and alice. 419 00:17:19,700 --> 00:17:22,650 They are really, technically, identical. 420 00:17:22,650 --> 00:17:24,880 They're identical and compatible. 421 00:17:24,880 --> 00:17:26,590 They have the same memory layout, 422 00:17:26,590 --> 00:17:28,440 the same field names, 423 00:17:28,440 --> 00:17:31,690 and ideally, if I have a value of type bill or alice, 424 00:17:31,690 --> 00:17:33,700 I should be able to assign them to each other 425 00:17:33,700 --> 00:17:35,770 without any integrity issues, right? 426 00:17:35,770 --> 00:17:36,780 There are none. 427 00:17:36,780 --> 00:17:38,970 Their memory layouts are identical. 428 00:17:38,970 --> 00:17:40,860 All right, so let's do the following then. 429 00:17:40,860 --> 00:17:42,380 I'm going to come back here 430 00:17:42,380 --> 00:17:46,430 and I'm going to create a variable of type bill. 431 00:17:46,430 --> 00:17:48,750 And a variable of type alice. 432 00:17:48,750 --> 00:17:51,400 Both set to their zero value. 433 00:17:51,400 --> 00:17:55,540 And I'm going to go ahead and assign alice to bill. 434 00:17:55,540 --> 00:17:57,340 Now the compiler will complain 435 00:17:57,340 --> 00:17:59,330 if I don't use these variables. 436 00:17:59,330 --> 00:18:02,220 And if the compiler, again, focusing 437 00:18:02,220 --> 00:18:04,710 or the language, again, focusing on readability. 438 00:18:04,710 --> 00:18:06,230 Don't declare a variable if you're not going to use it. 439 00:18:06,230 --> 00:18:07,530 Doesn't help with readability, 440 00:18:07,530 --> 00:18:08,860 so I'm just going to display it. 441 00:18:08,860 --> 00:18:10,040 But here's the question, 442 00:18:10,040 --> 00:18:12,270 we know bill and alice are identical. 443 00:18:12,270 --> 00:18:14,160 We know they are compatible, 444 00:18:14,160 --> 00:18:15,750 I do this assignment, 445 00:18:15,750 --> 00:18:17,480 and it shouldn't be a problem 446 00:18:17,480 --> 00:18:19,400 I know it, the compiler knows it. 447 00:18:19,400 --> 00:18:20,940 But look at what the compiler says, 448 00:18:20,940 --> 00:18:25,280 it says sorry, Bill, but you can't use A as type alice, 449 00:18:25,280 --> 00:18:27,000 as type bill in the assignment. 450 00:18:27,000 --> 00:18:30,590 I'm sorry, but we're not going to do this assignment. 451 00:18:30,590 --> 00:18:32,560 Now, there are lots of other languages 452 00:18:32,560 --> 00:18:34,180 that would've done this assignment. 453 00:18:34,180 --> 00:18:35,970 And when languages do this assignment, 454 00:18:35,970 --> 00:18:38,860 we call it an implicit conversion. 455 00:18:38,860 --> 00:18:40,270 We're going back to what Martian Thompson 456 00:18:40,270 --> 00:18:42,370 said about implicit conversion 457 00:18:42,370 --> 00:18:44,720 being like a Halloween special. 458 00:18:44,720 --> 00:18:47,520 Implicit conversion has caused a tremendous amount 459 00:18:47,520 --> 00:18:51,150 of pain for software over the decades. 460 00:18:51,150 --> 00:18:53,270 And, no, it's not going to cause us pain, 461 00:18:53,270 --> 00:18:54,990 necessarily with our struct types. 462 00:18:54,990 --> 00:18:56,620 Where it traditionally has caused pain, 463 00:18:56,620 --> 00:18:59,380 is when B is an int, a signed int. 464 00:18:59,380 --> 00:19:01,480 And A is an unsigned int. 465 00:19:01,480 --> 00:19:04,500 And the compiler allows the signed and unsigned variables 466 00:19:04,500 --> 00:19:06,070 to be assigned to each other. 467 00:19:06,070 --> 00:19:08,840 We know now there's going to be a loss of precision, 468 00:19:08,840 --> 00:19:11,330 and therefore, there's going to be data corruption. 469 00:19:11,330 --> 00:19:13,700 And so, though there are languages enterically, 470 00:19:13,700 --> 00:19:15,390 that have done implicit conversion 471 00:19:15,390 --> 00:19:19,070 when there is data that is identical and compatible, 472 00:19:19,070 --> 00:19:21,150 it's caused more problems than good. 473 00:19:21,150 --> 00:19:22,450 And Go says, look, when we're working 474 00:19:22,450 --> 00:19:25,430 with this concrete data, if the data is like this, 475 00:19:25,430 --> 00:19:28,170 bill and alice, right, we're based on these name types, 476 00:19:28,170 --> 00:19:31,560 I'm sorry, but I'm not going to do implicit conversion 477 00:19:31,560 --> 00:19:34,670 because historically it's caused more problems than good. 478 00:19:34,670 --> 00:19:37,120 But what if we really need to do this assignment? 479 00:19:37,120 --> 00:19:39,290 This is were conversion comes in. 480 00:19:39,290 --> 00:19:41,920 I love the idea of conversion in Go, 481 00:19:41,920 --> 00:19:46,020 because what it does, is it shows your intention. 482 00:19:46,020 --> 00:19:47,830 It's a way of telling the compiler, 483 00:19:47,830 --> 00:19:52,830 look, it is my intention that A does get assigned to B. 484 00:19:53,370 --> 00:19:55,310 And now there can't be any mistake. 485 00:19:55,310 --> 00:19:56,580 The compiler can't say, well, 486 00:19:56,580 --> 00:19:58,050 I thought you wanted to do this. 487 00:19:58,050 --> 00:19:58,883 No, no, no, no. 488 00:19:58,883 --> 00:20:01,220 Now the compiler's saying, if you really want to do this, 489 00:20:01,220 --> 00:20:04,890 then you explicitly, not implicitly, explicitly 490 00:20:04,890 --> 00:20:07,650 tell me through the conversion syntax. 491 00:20:07,650 --> 00:20:10,050 Show me that this was your intent. 492 00:20:10,050 --> 00:20:12,300 And there it is, we see conversion syntax. 493 00:20:12,300 --> 00:20:14,410 And now, if I do this, yes, 494 00:20:14,410 --> 00:20:16,220 we're going to be able to compile through. 495 00:20:16,220 --> 00:20:19,660 This is a beautiful, a beautiful piece of the language. 496 00:20:19,660 --> 00:20:21,490 Holding us in our code, 497 00:20:21,490 --> 00:20:24,480 to integrity and holding us to responsibility. 498 00:20:24,480 --> 00:20:26,350 When we're doing these types of assignments. 499 00:20:26,350 --> 00:20:28,750 But I've got an interesting question for you. 500 00:20:28,750 --> 00:20:32,200 We've got an e2 variable here, based on a struct 501 00:20:32,200 --> 00:20:36,230 that also is identical and compatible with bill and alice. 502 00:20:36,230 --> 00:20:39,980 What if I go ahead and I put e2 here? 503 00:20:39,980 --> 00:20:42,200 What will the compiler do? 504 00:20:42,200 --> 00:20:46,100 Will the compiler allow this, or still ask for conversion? 505 00:20:46,100 --> 00:20:47,920 Well, what your going to notice is the compiler 506 00:20:47,920 --> 00:20:50,020 doesn't ask for conversion here. 507 00:20:50,020 --> 00:20:54,280 And now, the big difference between A and e2 is that A 508 00:20:54,280 --> 00:20:57,360 was a name type, e2 is a literal type. 509 00:20:57,360 --> 00:20:58,880 When the type is named, 510 00:20:58,880 --> 00:21:00,970 there's going to be no implicit conversion. 511 00:21:00,970 --> 00:21:02,580 You're going to have to show your intent 512 00:21:02,580 --> 00:21:03,750 through conversion. 513 00:21:03,750 --> 00:21:05,773 But when the value like this, 514 00:21:05,773 --> 00:21:09,180 isn't named, it's a literal type, 515 00:21:09,180 --> 00:21:12,180 now we have this flexibility on assignment. 516 00:21:12,180 --> 00:21:15,050 Now again, it's kind of silly to do it with struct types. 517 00:21:15,050 --> 00:21:17,440 Ideally, you'll see this type of assignment 518 00:21:17,440 --> 00:21:20,100 with literal values, when we deal with functions. 519 00:21:20,100 --> 00:21:23,130 A function in Go is a value, it's a literal value. 520 00:21:23,130 --> 00:21:24,710 It's not a named type. 521 00:21:24,710 --> 00:21:27,090 And we pass functions to functions all over Go. 522 00:21:27,090 --> 00:21:29,440 Especially, we start talking about web APIs 523 00:21:29,440 --> 00:21:31,090 and middlewares and things like that. 524 00:21:31,090 --> 00:21:33,960 So, it makes you aware it's not really cumbersome. 525 00:21:33,960 --> 00:21:37,020 So I love the balance that the language is giving us here. 526 00:21:37,020 --> 00:21:39,270 If we're dealing with values of a name type, 527 00:21:39,270 --> 00:21:40,960 there is no implicit conversion. 528 00:21:40,960 --> 00:21:43,110 You've gotta use your conversion syntax, 529 00:21:43,110 --> 00:21:45,240 you've gotta show your intent. 530 00:21:45,240 --> 00:21:47,480 When there's, when we're dealing 531 00:21:47,480 --> 00:21:49,440 with that literal type value 532 00:21:49,440 --> 00:21:51,850 then we're gonna get a little bit more flexibility. 533 00:21:51,850 --> 00:21:54,220 Well, it's not really implicit there 534 00:21:54,220 --> 00:21:57,020 so much as we know that these things are compatible. 535 00:21:57,020 --> 00:21:59,030 We allow it to happen. 536 00:21:59,030 --> 00:21:59,863 And everything is safe. 537 00:21:59,863 --> 00:22:02,410 And we maintain these safe levels of integrity 538 00:22:02,410 --> 00:22:03,760 throughout our code. 539 00:22:03,760 --> 00:22:05,690 So remember that Go's not being novel here, 540 00:22:05,690 --> 00:22:10,310 the key word struct is our core way of defining 541 00:22:10,310 --> 00:22:12,370 user defined data in the language. 542 00:22:12,370 --> 00:22:13,660 We don't have the key word class, 543 00:22:13,660 --> 00:22:15,030 like you might have seen in other languages. 544 00:22:15,030 --> 00:22:15,880 We have struct. 545 00:22:15,880 --> 00:22:17,520 Struct defines that data. 546 00:22:17,520 --> 00:22:18,980 There's no implicit conversion 547 00:22:18,980 --> 00:22:22,250 when we start talking about the concrete data name types. 548 00:22:22,250 --> 00:22:23,650 We've got literal types. 549 00:22:23,650 --> 00:22:26,380 And the conversion syntax comes in handy here, 550 00:22:26,380 --> 00:22:30,410 when we're talking about doing that explicit 551 00:22:30,410 --> 00:22:31,810 conversion, okay? 552 00:22:31,810 --> 00:22:33,160 Then we also talked about the idea 553 00:22:33,160 --> 00:22:36,010 that I don't want to optimize for performance here. 554 00:22:36,010 --> 00:22:38,660 We always want to optimize for correctness. 555 00:22:38,660 --> 00:22:40,930 If I go back to the first example, 556 00:22:40,930 --> 00:22:44,230 you can see I want the fields to be laid out correctly. 557 00:22:44,230 --> 00:22:46,240 In other words, readability first. 558 00:22:46,240 --> 00:22:47,600 Grouping things together, 559 00:22:47,600 --> 00:22:49,970 but I want you to be able to understand the cost of things. 560 00:22:49,970 --> 00:22:52,360 And understanding how much memory's going to be allocated 561 00:22:52,360 --> 00:22:55,100 for a particular type helps you with understanding 562 00:22:55,100 --> 00:22:57,780 cost and therefore, we talked about alignments, 563 00:22:57,780 --> 00:22:59,323 We talked about padding. 564 00:23:00,170 --> 00:23:03,920 And we talked about that only unless you have a benchmark, 565 00:23:03,920 --> 00:23:05,690 that's showing you're using up too much memory, 566 00:23:05,690 --> 00:23:10,490 would I want to optimize that struct to minimize padding. 567 00:23:10,490 --> 00:23:13,110 I cannot stress this enough, 568 00:23:13,110 --> 00:23:16,260 because I've had too many clients come in after this lesson 569 00:23:16,260 --> 00:23:20,300 and restructure everything to reduce padding to the bottom. 570 00:23:20,300 --> 00:23:21,780 Please don't do that. 571 00:23:21,780 --> 00:23:24,250 I'm teaching you this so we can look at cost 572 00:23:24,250 --> 00:23:25,200 in terms of allocation. 573 00:23:25,200 --> 00:23:27,050 Not so we can restructure everything.