1 00:00:06,560 --> 00:00:08,780 - Alright let's go explore a little bit deeper 2 00:00:08,780 --> 00:00:11,700 the idea that we're passing concrete data 3 00:00:11,700 --> 00:00:13,330 across these program boundaries 4 00:00:13,330 --> 00:00:15,180 when we're using interface values. 5 00:00:15,180 --> 00:00:17,821 We could call this, you know, like implicit interface 6 00:00:17,821 --> 00:00:21,000 conversion, but again I really want to focus on the fact 7 00:00:21,000 --> 00:00:22,850 that it's the concrete data that we're moving 8 00:00:22,850 --> 00:00:24,360 and I want to look at more of the mechanics 9 00:00:24,360 --> 00:00:27,490 behind it just to solidify this a little bit more. 10 00:00:27,490 --> 00:00:29,830 So look I've got the mover interface, 11 00:00:29,830 --> 00:00:33,270 the locker interface, that's move, lock, and unlock. 12 00:00:33,270 --> 00:00:34,560 And then I've got the move locker, 13 00:00:34,560 --> 00:00:37,540 remember the composition of multiple interfaces. 14 00:00:37,540 --> 00:00:40,220 So a move locker knows how to move, lock, and unlock. 15 00:00:40,220 --> 00:00:42,590 We've got the three interfaces right there. 16 00:00:42,590 --> 00:00:45,230 Then I've defined a concrete type named bike. 17 00:00:45,230 --> 00:00:47,190 Now this is implementing using the, 18 00:00:47,190 --> 00:00:50,150 or declared using the empty struct, right? 19 00:00:50,150 --> 00:00:53,719 This is a case where you might want a method based API 20 00:00:53,719 --> 00:00:56,970 for some reason where there is no state, right? 21 00:00:56,970 --> 00:00:59,320 Maybe we're using bike as a name space. 22 00:00:59,320 --> 00:01:01,837 Gonna want to be very careful, remember functions 23 00:01:01,837 --> 00:01:04,560 are always gonna be more precise than methods 24 00:01:04,560 --> 00:01:06,669 but in this particular case since there's no state 25 00:01:06,669 --> 00:01:09,000 then the API would have to be as precise 26 00:01:09,000 --> 00:01:11,300 as any function, right? 27 00:01:11,300 --> 00:01:14,080 And we've got the implementation of move, lock, and unlock. 28 00:01:14,080 --> 00:01:17,290 So a bike is a mover a locker and a move locker. 29 00:01:17,290 --> 00:01:20,000 Okay great, now this is where things get fun. 30 00:01:20,000 --> 00:01:21,860 On line 49 and line 50 31 00:01:21,860 --> 00:01:24,060 we're declaring two interface values 32 00:01:24,060 --> 00:01:27,850 set to their zero value, there they are, 33 00:01:27,850 --> 00:01:29,630 that's the move locker. 34 00:01:29,630 --> 00:01:33,820 That's the mover right there, again set to it's zero value, 35 00:01:33,820 --> 00:01:35,370 a nil interface. 36 00:01:35,370 --> 00:01:37,899 And then we go ahead on line 54 and we construct 37 00:01:37,899 --> 00:01:41,960 a bike value, there it is, there's our bike right. 38 00:01:41,960 --> 00:01:43,960 And it's an empty struct but there's our bike 39 00:01:43,960 --> 00:01:47,100 and we assign it to the move locker interface. 40 00:01:47,100 --> 00:01:50,700 We can do that because we've implemented that 41 00:01:50,700 --> 00:01:52,740 and if we go back and look at the implementation 42 00:01:52,740 --> 00:01:55,460 of the interface it's using value semantics. 43 00:01:55,460 --> 00:01:56,293 Value semantics. 44 00:01:56,293 --> 00:01:59,480 So we can store copies of values and the addresses. 45 00:01:59,480 --> 00:02:01,600 And we're using value semantics here. 46 00:02:01,600 --> 00:02:03,840 So this is gonna say we have our bike 47 00:02:03,840 --> 00:02:07,310 and we're gonna point to that bike value right there, 48 00:02:07,310 --> 00:02:08,143 there it is. 49 00:02:08,143 --> 00:02:09,850 Remember it's the empty struct 50 00:02:09,850 --> 00:02:11,720 so I could argue that really what we have 51 00:02:11,720 --> 00:02:13,210 is a copy of the bike. 52 00:02:13,210 --> 00:02:15,560 We're using value semantics right? 53 00:02:15,560 --> 00:02:18,530 But since there's no construction anywhere else there, 54 00:02:18,530 --> 00:02:21,160 there's no variable, let's just look at it as 55 00:02:21,160 --> 00:02:22,210 there's our bike. 56 00:02:22,210 --> 00:02:24,458 Now what we're able to do on line 58 57 00:02:24,458 --> 00:02:28,520 is go ahead and say let's assign ML to M. 58 00:02:28,520 --> 00:02:31,460 Two interface values, but we're not really assigning 59 00:02:31,460 --> 00:02:32,540 the interface values. 60 00:02:32,540 --> 00:02:34,860 Remember the only thing that's real is the bike. 61 00:02:34,860 --> 00:02:36,930 So what we're really saying is let's store 62 00:02:36,930 --> 00:02:40,220 the concrete type bike inside of M. 63 00:02:40,220 --> 00:02:44,310 And the compiler knows whether or not this concrete type 64 00:02:44,310 --> 00:02:46,870 has all of the behaviors to satisfy M 65 00:02:46,870 --> 00:02:48,700 because the interface declarations making 66 00:02:48,700 --> 00:02:52,421 and we know that any concrete type inside of move locker 67 00:02:52,421 --> 00:02:55,780 must be able to move, lock, and unlock. 68 00:02:55,780 --> 00:02:58,360 We know all three behaviors exist, 69 00:02:58,360 --> 00:02:59,900 move, lock, and unlock. 70 00:02:59,900 --> 00:03:02,890 Therefore we can do the assignment because the only behavior 71 00:03:02,890 --> 00:03:06,570 we need is move, and so now we say we've got a bike 72 00:03:06,570 --> 00:03:09,050 in here and boom there we go. 73 00:03:09,050 --> 00:03:11,180 We can do that assignment. 74 00:03:11,180 --> 00:03:14,400 However we can't go in the other direction. 75 00:03:14,400 --> 00:03:17,530 I can't assign a mover to a move locker. 76 00:03:17,530 --> 00:03:20,713 Because when it comes to the mover the only behavior 77 00:03:20,713 --> 00:03:23,730 that we are aware of is move. 78 00:03:23,730 --> 00:03:26,260 This is the only thing that we know about. 79 00:03:26,260 --> 00:03:29,660 We don't know if there's a lock and an unlock in here. 80 00:03:29,660 --> 00:03:31,280 When we look at the concrete data 81 00:03:31,280 --> 00:03:33,760 from the mover perspective. 82 00:03:33,760 --> 00:03:35,400 When we look at it from the move locker 83 00:03:35,400 --> 00:03:36,870 perspective we know. 84 00:03:36,870 --> 00:03:39,160 So we're not able to do that assignment. 85 00:03:39,160 --> 00:03:42,420 Now Go has something else that's very special. 86 00:03:42,420 --> 00:03:45,050 It's called type assertions. 87 00:03:45,050 --> 00:03:48,894 And a type assertion in Go allows you to ask a question 88 00:03:48,894 --> 00:03:52,200 about what is stored about the concrete data 89 00:03:52,200 --> 00:03:54,280 that is stored inside the interface. 90 00:03:54,280 --> 00:03:55,770 Allows you to ask a question. 91 00:03:55,770 --> 00:03:57,070 The question we're asking here, 92 00:03:57,070 --> 00:03:59,900 and look at the syntax, the interface value dot 93 00:03:59,900 --> 00:04:02,160 whatever type we want to ask the question around. 94 00:04:02,160 --> 00:04:03,580 What we're asking, and this happens 95 00:04:03,580 --> 00:04:05,797 not at compile time, everything I've told you so far 96 00:04:05,797 --> 00:04:07,730 can be checked at compile time. 97 00:04:07,730 --> 00:04:09,637 Type assertions happen at run time. 98 00:04:09,637 --> 00:04:12,260 What we're seeing on line 76 is 99 00:04:12,260 --> 00:04:14,450 I believe when this code runs 100 00:04:14,450 --> 00:04:16,460 in that moment in time and space 101 00:04:16,460 --> 00:04:19,240 that the concrete value that's sitting inside 102 00:04:19,240 --> 00:04:21,560 of M is a bike value. 103 00:04:21,560 --> 00:04:22,840 That's what we're asking. 104 00:04:22,840 --> 00:04:26,320 Now if it's true that there's a bike value in there, 105 00:04:26,320 --> 00:04:28,440 since we're using value semantics, 106 00:04:28,440 --> 00:04:32,840 B will be a copy of that bike that was stored 107 00:04:32,840 --> 00:04:34,500 it will be a copy. 108 00:04:34,500 --> 00:04:36,620 If it's wrong then we panic. 109 00:04:36,620 --> 00:04:38,490 We have an integrity issue. 110 00:04:38,490 --> 00:04:41,240 You're saying absolutely there needs to be a bike in here 111 00:04:41,240 --> 00:04:43,330 and there isn't alright. 112 00:04:43,330 --> 00:04:46,230 But there is a second form to the type assertion 113 00:04:47,220 --> 00:04:49,461 which is this OK value. 114 00:04:49,461 --> 00:04:51,670 And this is a Boolean flag. 115 00:04:51,670 --> 00:04:54,760 And we want to use this when we may not necessarily 116 00:04:54,760 --> 00:04:56,906 have a concrete value of that type. 117 00:04:56,906 --> 00:04:59,690 Now what this is saying is I believe 118 00:04:59,690 --> 00:05:02,000 there's a bike value inside of M. 119 00:05:02,000 --> 00:05:04,410 If there is great, okay it's true, 120 00:05:04,410 --> 00:05:06,100 B is a copy of the bike. 121 00:05:06,100 --> 00:05:09,210 But if OK is false, then there isn't. 122 00:05:09,210 --> 00:05:10,710 Do not panic. 123 00:05:10,710 --> 00:05:14,060 We know that there's a chance that that might not be true. 124 00:05:14,060 --> 00:05:16,710 And then B isn't a copy of the bike, 125 00:05:16,710 --> 00:05:20,280 it's set to it's zero value or zero value bike. 126 00:05:20,280 --> 00:05:22,942 Type assertions are very very powerful. 127 00:05:22,942 --> 00:05:27,130 And I want to show you some examples of type assertions 128 00:05:27,130 --> 00:05:29,530 and how they are working pretty well 129 00:05:29,530 --> 00:05:30,920 in the standard library. 130 00:05:30,920 --> 00:05:34,000 One of the best places to look is I'm gonna do a search 131 00:05:34,000 --> 00:05:35,910 on Golang IO. 132 00:05:35,910 --> 00:05:38,320 I'm gonna look at the IO package in Go 133 00:05:38,320 --> 00:05:41,080 and I'm gonna come in here and I'm gonna look for 134 00:05:41,080 --> 00:05:42,950 the copy function. 135 00:05:42,950 --> 00:05:45,560 Now anytime you're looking at Go documentation 136 00:05:45,560 --> 00:05:47,680 if you find something you want and you want to look 137 00:05:47,680 --> 00:05:50,270 at the source code, all you gotta do is click on that 138 00:05:50,270 --> 00:05:51,103 header link. 139 00:05:51,103 --> 00:05:52,520 If you look again I found it, 140 00:05:52,520 --> 00:05:55,380 I click on the header link, brings me right to the code. 141 00:05:55,380 --> 00:05:59,830 I really want to look at the unexported implementation 142 00:05:59,830 --> 00:06:01,440 of this copy buffer. 143 00:06:01,440 --> 00:06:04,440 Notice that copy buffer is saying give me any concrete 144 00:06:04,440 --> 00:06:06,360 piece of data that implements writer, 145 00:06:06,360 --> 00:06:08,770 any concrete data that implements reader. 146 00:06:08,770 --> 00:06:11,430 Look at the very first if statement. 147 00:06:11,430 --> 00:06:12,850 And this is interesting. 148 00:06:12,850 --> 00:06:15,860 What it's saying here is the following. 149 00:06:15,860 --> 00:06:20,660 If the concrete data stored inside of the reader variable, 150 00:06:20,660 --> 00:06:24,340 right, source, also implements this other behavior, 151 00:06:24,340 --> 00:06:26,910 notice we're type asserting now not to the concrete 152 00:06:26,910 --> 00:06:30,020 but to another behavior, another interface type. 153 00:06:30,020 --> 00:06:33,770 If the concrete data also implements this other behavior 154 00:06:33,770 --> 00:06:37,610 then let's bypass the default behavior of copy 155 00:06:37,610 --> 00:06:39,890 and use this custom one. 156 00:06:39,890 --> 00:06:42,610 Again what we're able to do is override 157 00:06:42,610 --> 00:06:46,910 this default copy behavior by allowing the user 158 00:06:46,910 --> 00:06:49,380 to have their own implementation of things. 159 00:06:49,380 --> 00:06:52,330 Type assertions are very very powerful as part 160 00:06:52,330 --> 00:06:54,950 of an API design because that means you can design an API 161 00:06:54,950 --> 00:06:57,780 that has a default implementation 162 00:06:57,780 --> 00:07:02,110 but then use interfaces to help override the default. 163 00:07:02,110 --> 00:07:04,980 In fact I'm gonna show you another example of that 164 00:07:04,980 --> 00:07:07,373 exactly looking at this code right here. 165 00:07:09,040 --> 00:07:13,530 Now here I've got a concrete type named user. 166 00:07:13,530 --> 00:07:15,830 When user implements a method called string 167 00:07:15,830 --> 00:07:18,090 using pointer semantics, actually a little more 168 00:07:18,090 --> 00:07:19,650 is going on here. 169 00:07:19,650 --> 00:07:22,530 This method named string that returns a string 170 00:07:22,530 --> 00:07:26,680 implements the stringer interface in the fmt package. 171 00:07:26,680 --> 00:07:30,670 This is a way of overriding the default output behavior 172 00:07:30,670 --> 00:07:32,170 of the fmt package. 173 00:07:32,170 --> 00:07:34,900 But we're using pointer semantics which means what? 174 00:07:34,900 --> 00:07:37,380 Only pointers satisfy the interface. 175 00:07:37,380 --> 00:07:41,160 If I share pointers with fmt we get to override the default. 176 00:07:41,160 --> 00:07:43,610 I share values with fmt, we don't, 177 00:07:43,610 --> 00:07:45,080 we'll get the default behavior. 178 00:07:45,080 --> 00:07:47,380 And let's see that happen live here. 179 00:07:47,380 --> 00:07:50,450 So there I am creating a value of type user 180 00:07:50,450 --> 00:07:53,700 and then I use my value semantics on line 29 181 00:07:53,700 --> 00:07:57,130 and I use my pointer semantics on line 30. 182 00:07:57,130 --> 00:07:59,800 Well using value semantics when we implement 183 00:07:59,800 --> 00:08:02,710 the interface using our pointer receiver means that 184 00:08:02,710 --> 00:08:06,280 we should see the default output which is what we do. 185 00:08:06,280 --> 00:08:09,850 That's the default formatting for a struct value. 186 00:08:09,850 --> 00:08:12,820 But because we now passed the address of U 187 00:08:12,820 --> 00:08:16,700 we're able to override the default thanks to this method 188 00:08:16,700 --> 00:08:17,960 being implemented. 189 00:08:17,960 --> 00:08:19,730 Basically the call to print 190 00:08:19,730 --> 00:08:21,940 is doing that same type assertion. 191 00:08:21,940 --> 00:08:24,920 It's saying hey this concrete data that we passed in, 192 00:08:24,920 --> 00:08:27,890 does it also implement that string method? 193 00:08:27,890 --> 00:08:30,501 If it doesn't, like on line 29, no big deal. 194 00:08:30,501 --> 00:08:32,190 We have our default implementation. 195 00:08:32,190 --> 00:08:34,100 But if it does like on line 30 196 00:08:34,100 --> 00:08:35,920 we get to override the default. 197 00:08:35,920 --> 00:08:38,519 So this is a very powerful API design 198 00:08:38,519 --> 00:08:40,970 through type assertions because it allows 199 00:08:40,970 --> 00:08:43,430 you to have a standard implementation for something 200 00:08:43,430 --> 00:08:46,860 but not bind the user to that, tell the user hey 201 00:08:46,860 --> 00:08:48,540 if you got a better way to do it because maybe 202 00:08:48,540 --> 00:08:50,297 you're on this OS or on this architecture, 203 00:08:50,297 --> 00:08:52,800 or you just know better than I do, 204 00:08:52,800 --> 00:08:55,010 then I'm gonna give you the opportunity to override it. 205 00:08:55,010 --> 00:08:57,680 Type assertions, remember they happen at run time 206 00:08:57,680 --> 00:09:02,073 and they're very powerful parts of designing an API.