1 00:00:06,630 --> 00:00:08,550 - As we saw in the previous demo, 2 00:00:08,550 --> 00:00:11,520 a thread closure, if it's going to capture external state, 3 00:00:11,520 --> 00:00:15,150 must do so by moving ownership into the closure. 4 00:00:15,150 --> 00:00:16,500 So this is the example we saw 5 00:00:16,500 --> 00:00:19,560 in the previous demo and it works fine. 6 00:00:19,560 --> 00:00:21,690 I've got a vector here, V, 7 00:00:21,690 --> 00:00:25,620 and my thread, which spawns disclosure, 8 00:00:25,620 --> 00:00:28,170 I've got a for in loop in here, 9 00:00:28,170 --> 00:00:30,210 and when you use a for in loop with V, 10 00:00:30,210 --> 00:00:31,950 like I've written it here, 11 00:00:31,950 --> 00:00:35,340 then this for loop requires ownership of V 12 00:00:35,340 --> 00:00:38,933 which means V will be moved into the closure, okay? 13 00:00:40,290 --> 00:00:43,470 So the closure will acquire ownership of the vector. 14 00:00:43,470 --> 00:00:45,480 And that's good because 15 00:00:45,480 --> 00:00:48,150 if this external function disappears quite quickly, 16 00:00:48,150 --> 00:00:49,980 if it returns quite quickly, 17 00:00:49,980 --> 00:00:53,400 it's important that the closure owns it's data 18 00:00:53,400 --> 00:00:55,470 so that if it outlives the function 19 00:00:55,470 --> 00:00:57,603 it still owns the data that it's using. 20 00:00:58,890 --> 00:00:59,723 Right. 21 00:00:59,723 --> 00:01:03,300 So a thread closure is not allowed to capture external state 22 00:01:03,300 --> 00:01:05,940 by borrowing it into the closure. 23 00:01:05,940 --> 00:01:07,890 So we'd get a compiler error if we tried. 24 00:01:07,890 --> 00:01:09,870 The compiler will actually stop us 25 00:01:09,870 --> 00:01:13,230 from trying to borrow into a thread closure. 26 00:01:13,230 --> 00:01:14,280 Have a look at this example. 27 00:01:14,280 --> 00:01:15,570 It's quite similar. 28 00:01:15,570 --> 00:01:16,950 There's a key difference. 29 00:01:16,950 --> 00:01:21,480 I've got an ampersand here instead of a just V. 30 00:01:21,480 --> 00:01:23,310 So the code I've tried to write, 31 00:01:23,310 --> 00:01:27,750 here's my external vector, and in my thread closure, 32 00:01:27,750 --> 00:01:31,350 I've tried to capture a reference to V. 33 00:01:31,350 --> 00:01:34,650 I've tried to borrow V, okay? 34 00:01:34,650 --> 00:01:37,590 But the compiler won't allow it. 35 00:01:37,590 --> 00:01:40,080 The compiler will actually give an error message here. 36 00:01:40,080 --> 00:01:43,020 It'll say a thread closure. 37 00:01:43,020 --> 00:01:45,930 Even if you try to borrow a thread closure, 38 00:01:45,930 --> 00:01:47,670 it's not allowed to borrow, 39 00:01:47,670 --> 00:01:49,650 because you'd end up with a reference 40 00:01:49,650 --> 00:01:52,890 to something that might not live long enough. 41 00:01:52,890 --> 00:01:55,350 As I've said, if this thread 42 00:01:55,350 --> 00:01:57,660 is going to last for 10 seconds, 43 00:01:57,660 --> 00:02:00,870 but the external function is gonna terminate quite quickly, 44 00:02:00,870 --> 00:02:03,060 if it let us do this, the compiler, 45 00:02:03,060 --> 00:02:04,140 we'd end up with a reference 46 00:02:04,140 --> 00:02:07,620 to something that's no longer there, a dangling reference. 47 00:02:07,620 --> 00:02:09,870 This is actually enforced at the language level. 48 00:02:09,870 --> 00:02:13,110 You're not allowed to have dangling references in rest. 49 00:02:13,110 --> 00:02:16,650 And that's part of the ethos of rest, really, is safety, 50 00:02:16,650 --> 00:02:18,360 stopping us from doing things 51 00:02:18,360 --> 00:02:20,310 that other languages have let us do, 52 00:02:20,310 --> 00:02:22,143 but then penalize us by crashing. 53 00:02:23,430 --> 00:02:25,440 Right, so what do you do then? 54 00:02:25,440 --> 00:02:27,015 What if you have got code like this 55 00:02:27,015 --> 00:02:30,060 which is attempting to borrow, 56 00:02:30,060 --> 00:02:31,770 but it's not allowed to borrow? 57 00:02:31,770 --> 00:02:33,390 In that kind of case 58 00:02:33,390 --> 00:02:36,990 where you could get away with borrowing state, 59 00:02:36,990 --> 00:02:40,290 the compiler insists that a movement occurs, 60 00:02:40,290 --> 00:02:43,830 you can actually force the compiler to do a move. 61 00:02:43,830 --> 00:02:46,260 There's a move keyword, would you believe, 62 00:02:46,260 --> 00:02:47,880 which you put here, 63 00:02:47,880 --> 00:02:51,450 you put it before the closure when you spawn it, 64 00:02:51,450 --> 00:02:54,150 and regardless of how you're using the variable here, 65 00:02:54,150 --> 00:02:57,450 I mean technically, I'm using for item 66 00:02:57,450 --> 00:03:00,030 and I've got a reference to V here. 67 00:03:00,030 --> 00:03:03,270 So in terms of actual usage, the compiler would guess 68 00:03:03,270 --> 00:03:05,670 that a borrower would be sufficient. 69 00:03:05,670 --> 00:03:07,800 Of course, the other part of the compiler would say, 70 00:03:07,800 --> 00:03:09,930 oh no you don't because borrowing is dangerous 71 00:03:09,930 --> 00:03:11,480 because of dangling references. 72 00:03:12,540 --> 00:03:15,240 So to avoid that other part of the error, 73 00:03:15,240 --> 00:03:16,800 we can use the move keyword. 74 00:03:16,800 --> 00:03:18,600 The move keyword is where 75 00:03:18,600 --> 00:03:21,870 under other circumstances, the compiler would give an error 76 00:03:21,870 --> 00:03:24,600 because borrowing isn't really very sensible. 77 00:03:24,600 --> 00:03:28,260 The move keyword will force a move to occur, okay? 78 00:03:28,260 --> 00:03:31,290 So this factor will be forcibly moved 79 00:03:31,290 --> 00:03:34,470 and therefore claimed ownership inside the closure. 80 00:03:34,470 --> 00:03:35,970 So even if this external function 81 00:03:35,970 --> 00:03:37,890 terminated almost immediately, 82 00:03:37,890 --> 00:03:41,730 the data would be moved forcibly into disclosure 83 00:03:41,730 --> 00:03:43,470 and it would live long enough 84 00:03:43,470 --> 00:03:45,303 for that thread to use it. 85 00:03:46,620 --> 00:03:49,980 Right, so only one extra keyword in this example, 86 00:03:49,980 --> 00:03:51,720 but quite a profound difference. 87 00:03:51,720 --> 00:03:54,360 So let's run the demo in the usual project. 88 00:03:54,360 --> 00:03:55,650 We're going to look at main, 89 00:03:55,650 --> 00:03:57,840 and then we'll have a look at demo_capturing_state 90 00:03:57,840 --> 00:03:59,430 using an explicit move 91 00:03:59,430 --> 00:04:01,620 where we have to forcibly tell the compiler 92 00:04:01,620 --> 00:04:04,323 to move data to avoid dangling references. 93 00:04:06,210 --> 00:04:08,850 So main, this is the code. 94 00:04:08,850 --> 00:04:11,850 I'm going to do run demo_capturing_state 95 00:04:11,850 --> 00:04:13,443 via an explicit move. 96 00:04:15,240 --> 00:04:16,833 And then here's the code. 97 00:04:18,060 --> 00:04:21,742 So I've gone into overdrive with my comments here. 98 00:04:21,742 --> 00:04:23,460 I hope it's grammatically correct. 99 00:04:23,460 --> 00:04:26,673 So here's my main code up here for this part of the demo. 100 00:04:27,660 --> 00:04:29,520 Very similar to the previous example. 101 00:04:29,520 --> 00:04:31,920 I call this do_some_work function 102 00:04:31,920 --> 00:04:35,880 which will spawn a thread but then terminate 103 00:04:35,880 --> 00:04:38,910 or return immediately with a thread handle. 104 00:04:38,910 --> 00:04:41,010 So it'll kick off this other thread 105 00:04:41,010 --> 00:04:42,810 which will take a while to spin 106 00:04:42,810 --> 00:04:45,360 but the main thread returns the handle immediately. 107 00:04:45,360 --> 00:04:47,400 Remember, when you have an expression 108 00:04:47,400 --> 00:04:51,000 at the end of a function, it's like saying return that, 109 00:04:51,000 --> 00:04:52,080 but it's more idiomatic 110 00:04:52,080 --> 00:04:54,060 to just read it this way and (indistinct). 111 00:04:54,060 --> 00:04:57,540 Returns the handle, back up to our main code up here, 112 00:04:57,540 --> 00:04:59,220 and our main code then just sits there 113 00:04:59,220 --> 00:05:00,690 busy, doing nothing, really, 114 00:05:00,690 --> 00:05:02,640 waiting for that thread to finish. 115 00:05:02,640 --> 00:05:04,050 So let's see what that thread actually does. 116 00:05:04,050 --> 00:05:07,190 Here's my vector with elements 100 to 105. 117 00:05:09,004 --> 00:05:09,837 Okay. 118 00:05:09,837 --> 00:05:13,470 So as before, when you have a closure, 119 00:05:13,470 --> 00:05:15,300 which captures a variable, 120 00:05:15,300 --> 00:05:18,900 and we do have a closure, which captures a variable, 121 00:05:18,900 --> 00:05:23,610 V is the external variable, and we are capturing it here, 122 00:05:23,610 --> 00:05:27,360 so the compiler will analyze usage by us 123 00:05:27,360 --> 00:05:30,060 of that variable in the closure 124 00:05:30,060 --> 00:05:32,970 to decide whether a borrow, a multiple borrow 125 00:05:32,970 --> 00:05:35,790 or a move would be required. 126 00:05:35,790 --> 00:05:38,790 So the compiler infers how to capture V, 127 00:05:38,790 --> 00:05:41,610 dependent on how it's used in the closure, right? 128 00:05:41,610 --> 00:05:43,590 And that's what I just said. 129 00:05:43,590 --> 00:05:45,930 In this example, the compiler would guess, 130 00:05:45,930 --> 00:05:48,810 I'm ignoring the move keyword for now, okay? 131 00:05:48,810 --> 00:05:50,700 So let's ignore that for now. 132 00:05:50,700 --> 00:05:52,890 So in the usage that the compiler sees 133 00:05:52,890 --> 00:05:55,440 in the closure as it stands, 134 00:05:55,440 --> 00:05:59,100 the compiler would guess that a borrow would be sufficient 135 00:05:59,100 --> 00:06:02,550 because the for end loop now just borrows vector V. 136 00:06:02,550 --> 00:06:05,730 It borrows it rather than actually 137 00:06:05,730 --> 00:06:07,080 acquiring ownership of it. 138 00:06:07,080 --> 00:06:09,543 It just passes a reference into the for loop. 139 00:06:11,160 --> 00:06:14,520 And that's the problem that the move keyword resolves. 140 00:06:14,520 --> 00:06:18,513 If the compiler did decide that a borrow was sufficient, 141 00:06:19,440 --> 00:06:21,360 this would give us a different compiler error 142 00:06:21,360 --> 00:06:23,220 because of thread timing. 143 00:06:23,220 --> 00:06:24,900 The closure of the thread 144 00:06:24,900 --> 00:06:27,690 might outlive the scope of the current function 145 00:06:27,690 --> 00:06:29,730 and then we'd have a dangling reference to V 146 00:06:29,730 --> 00:06:31,350 externally that it disappeared. 147 00:06:31,350 --> 00:06:34,050 So if I didn't have this move keyword, 148 00:06:34,050 --> 00:06:35,910 and actually get an error from the compiler, 149 00:06:35,910 --> 00:06:37,320 should we see that first? 150 00:06:37,320 --> 00:06:38,580 I'll delete that move keyword, 151 00:06:38,580 --> 00:06:41,610 because let's say I didn't know about it, okay? 152 00:06:41,610 --> 00:06:43,110 So I've just got that code. 153 00:06:43,110 --> 00:06:44,610 I'm gonna get a compiler error 154 00:06:48,243 --> 00:06:49,710 and the compiler error will occur... 155 00:06:49,710 --> 00:06:50,850 Well, difficult to tell, actually. 156 00:06:50,850 --> 00:06:52,530 I think the compiler's gonna give me an error 157 00:06:52,530 --> 00:06:54,600 on line 22, would be my guess. 158 00:06:54,600 --> 00:06:55,433 Let's see. 159 00:07:00,457 --> 00:07:01,860 Yeah, there we go. 160 00:07:01,860 --> 00:07:04,590 So when I spawned the thread, 161 00:07:04,590 --> 00:07:06,190 there's the start of my closure, 162 00:07:07,050 --> 00:07:09,810 it realizes, looking at it here, 163 00:07:09,810 --> 00:07:11,040 the compiler on the one hand 164 00:07:11,040 --> 00:07:13,260 would think a borrow would be sufficient, 165 00:07:13,260 --> 00:07:15,420 and then it says, okay, so that's a reference 166 00:07:15,420 --> 00:07:17,070 to something external. 167 00:07:17,070 --> 00:07:20,100 You're referring to something you, the spawn thread, 168 00:07:20,100 --> 00:07:22,650 might outlive the borrowed value V. 169 00:07:22,650 --> 00:07:23,699 Okay? 170 00:07:23,699 --> 00:07:25,440 so you borrow it here. 171 00:07:25,440 --> 00:07:29,820 What if your thread outlives V? 172 00:07:29,820 --> 00:07:32,730 Then you're left with a dangling reference. 173 00:07:32,730 --> 00:07:35,472 So doesn't like it at all. 174 00:07:35,472 --> 00:07:38,340 But being rest, it doesn't just beat you about the face, 175 00:07:38,340 --> 00:07:40,260 it tries to be constructive. 176 00:07:40,260 --> 00:07:43,710 And it says maybe to force the closure 177 00:07:43,710 --> 00:07:46,230 to take ownership of V and anything else, 178 00:07:46,230 --> 00:07:48,090 use the move keyword. 179 00:07:48,090 --> 00:07:49,860 I've never come across a language 180 00:07:49,860 --> 00:07:51,240 which makes in the compiler, 181 00:07:51,240 --> 00:07:54,240 it makes such an effort to actually explain stuff. 182 00:07:54,240 --> 00:07:56,610 It's kind of the opposite of the C++ compiler 183 00:07:56,610 --> 00:07:58,680 which is as terse as it could possibly be. 184 00:07:58,680 --> 00:08:02,430 It suggests adding the move keyword here. 185 00:08:02,430 --> 00:08:05,100 Well, that seems like a good idea, so let's do it. 186 00:08:05,100 --> 00:08:05,933 Move. 187 00:08:05,933 --> 00:08:08,610 So regardless of the usage pattern 188 00:08:08,610 --> 00:08:11,910 that the compiler might think, reference, no, 189 00:08:11,910 --> 00:08:13,080 it's gonna be a movement 190 00:08:13,080 --> 00:08:15,720 in order for us to own the data inside the thread 191 00:08:15,720 --> 00:08:18,180 for as long as the thread needs it. 192 00:08:18,180 --> 00:08:20,190 Of course, that's a double-edged sword 193 00:08:20,190 --> 00:08:23,880 because now that that vector has been claimed, 194 00:08:23,880 --> 00:08:26,310 ownership has been claimed by the closure, 195 00:08:26,310 --> 00:08:28,830 it's been moved away from the outer function, 196 00:08:28,830 --> 00:08:31,500 we can't use this variable anymore, 197 00:08:31,500 --> 00:08:33,150 and we discussed that previously, didn't we? 198 00:08:33,150 --> 00:08:35,910 Now that that variable's been moved into the closure, 199 00:08:35,910 --> 00:08:38,490 we can't then use V afterwards, 200 00:08:38,490 --> 00:08:40,260 and we've seen what would happen if we tried. 201 00:08:40,260 --> 00:08:41,640 You get a compiler error saying, 202 00:08:41,640 --> 00:08:43,620 but that value's moved away. 203 00:08:43,620 --> 00:08:45,273 Get your act together, dude. 204 00:08:46,200 --> 00:08:48,780 Anyway, I think we're in a good state to run this now. 205 00:08:48,780 --> 00:08:50,070 Do some work. 206 00:08:50,070 --> 00:08:51,670 Here's the function I've called. 207 00:08:52,609 --> 00:08:56,100 (indistinct) handle, pretty quickly 208 00:08:56,100 --> 00:08:58,300 it'll return the joint handle at the bottom. 209 00:08:59,370 --> 00:09:01,230 So the main thread can wait. 210 00:09:01,230 --> 00:09:03,210 Inside here, having captured V, 211 00:09:03,210 --> 00:09:06,390 it's quite happily going to be displaying the values 212 00:09:06,390 --> 00:09:10,263 in a 500 millisecond kind of delay. 213 00:09:11,250 --> 00:09:13,080 Okay, so should we just run it? 214 00:09:13,080 --> 00:09:15,007 And I think I fixed all the bugs now 215 00:09:15,007 --> 00:09:16,530 that I've been mentioning. 216 00:09:16,530 --> 00:09:18,600 Let's just see if it works. 217 00:09:18,600 --> 00:09:19,433 Yeah. 218 00:09:20,310 --> 00:09:24,690 So my inner thread, my background thread was busy. 219 00:09:24,690 --> 00:09:28,230 Every 500 milliseconds, it was displaying those things. 220 00:09:28,230 --> 00:09:33,230 Then it terminated, and my main thread then resumed 221 00:09:33,450 --> 00:09:35,460 and that's the end of that. 222 00:09:35,460 --> 00:09:37,653 That's all, folks, for this demo at least.