1 00:00:06,540 --> 00:00:08,667 - Okay, so you can force a closure 2 00:00:08,667 --> 00:00:11,010 to capture a variable by value. 3 00:00:11,010 --> 00:00:12,736 You can forcibly say, move. 4 00:00:12,736 --> 00:00:14,492 You can tell the compiler 5 00:00:14,492 --> 00:00:17,190 no matter what you might think about the closure. 6 00:00:17,190 --> 00:00:18,570 You might think that the closure 7 00:00:18,570 --> 00:00:20,340 can just capture by reference, 8 00:00:20,340 --> 00:00:22,477 but I'm telling you, don't do that. 9 00:00:22,477 --> 00:00:24,120 You must do a move. 10 00:00:24,120 --> 00:00:26,340 You can forcibly tell the compiler 11 00:00:26,340 --> 00:00:29,490 to move ownership into the closure, 12 00:00:29,490 --> 00:00:32,580 and that's useful when you're spawning 13 00:00:32,580 --> 00:00:33,930 another thread. 14 00:00:33,930 --> 00:00:34,763 Okay? 15 00:00:34,763 --> 00:00:37,440 In Rust, one thread can spawn another thread. 16 00:00:37,440 --> 00:00:39,533 You can create another thread. 17 00:00:39,533 --> 00:00:42,386 The thread executes code in a closure, 18 00:00:42,386 --> 00:00:43,656 you give it a closure, 19 00:00:43,656 --> 00:00:46,987 and it executes that code on a separate thread. 20 00:00:46,987 --> 00:00:48,079 Now, the thing is, 21 00:00:48,079 --> 00:00:51,180 the thread, the closure that you've given it 22 00:00:51,180 --> 00:00:52,424 might be long living 23 00:00:52,424 --> 00:00:56,220 and it might outlive the outer function. 24 00:00:56,220 --> 00:00:57,660 So if there were any variables 25 00:00:57,660 --> 00:00:59,221 in the external function, 26 00:00:59,221 --> 00:01:01,023 they must live long enough 27 00:01:01,023 --> 00:01:02,820 for the closure to complete. 28 00:01:02,820 --> 00:01:04,381 And the way you achieve that 29 00:01:04,381 --> 00:01:08,852 is by moving captive variables into the closure. 30 00:01:08,852 --> 00:01:09,702 Okay? 31 00:01:09,702 --> 00:01:13,620 Basically the thread must own those variables 32 00:01:13,620 --> 00:01:15,660 for as long as the thread lives. 33 00:01:15,660 --> 00:01:18,090 It can't just rely on the outer function 34 00:01:18,090 --> 00:01:20,040 living long enough because it could terminate 35 00:01:20,040 --> 00:01:21,240 quite quickly. 36 00:01:21,240 --> 00:01:23,340 So if the thread does need to capture 37 00:01:23,340 --> 00:01:24,173 any variables, 38 00:01:24,173 --> 00:01:25,667 it must do it via a move, 39 00:01:25,667 --> 00:01:27,496 so that the variables live long enough 40 00:01:27,496 --> 00:01:29,730 for the thread to complete. 41 00:01:29,730 --> 00:01:32,111 So the syntax of doing a forceful move 42 00:01:32,111 --> 00:01:33,815 looks like this. 43 00:01:33,815 --> 00:01:35,602 I've got some outer context, 44 00:01:35,602 --> 00:01:38,728 with a message that might have a very short lifetime. 45 00:01:38,728 --> 00:01:42,411 I tell, when I call the spawn method, 46 00:01:42,411 --> 00:01:45,509 the standard crate has a thread module 47 00:01:45,509 --> 00:01:47,193 which has a spawn function. 48 00:01:47,193 --> 00:01:49,431 And the spawn function takes a closure 49 00:01:49,431 --> 00:01:53,700 to say run this code on a separate thread. 50 00:01:53,700 --> 00:01:55,170 So it displays the message, 51 00:01:55,170 --> 00:01:57,960 it then waits five seconds. 52 00:01:57,960 --> 00:02:00,513 So this closure is long lived, 53 00:02:01,500 --> 00:02:04,039 and then it prints the message afterwards. 54 00:02:04,039 --> 00:02:06,701 If I hadn't used the move keyword here, 55 00:02:06,701 --> 00:02:08,997 well it wouldn't compile, first of all. 56 00:02:08,997 --> 00:02:11,883 But if I hadn't put the move keyword here, 57 00:02:13,650 --> 00:02:15,280 conceptually, what could have happened 58 00:02:15,280 --> 00:02:18,600 is that the, let's imagine that the compiler 59 00:02:18,600 --> 00:02:20,130 didn't understand properly. 60 00:02:20,130 --> 00:02:21,367 The compiler might just say, 61 00:02:21,367 --> 00:02:22,560 "Oh, all he's simply doing 62 00:02:22,560 --> 00:02:23,820 is printing the message. 63 00:02:23,820 --> 00:02:25,050 So maybe what I'll do, 64 00:02:25,050 --> 00:02:26,683 is I'll just have a reference 65 00:02:26,683 --> 00:02:31,200 to the external variable up there like that." 66 00:02:31,200 --> 00:02:32,340 The problem being, of course, 67 00:02:32,340 --> 00:02:34,680 that this closure is gonna take five seconds 68 00:02:34,680 --> 00:02:37,484 to complete, and within that five seconds, 69 00:02:37,484 --> 00:02:40,038 this external function could have returned, 70 00:02:40,038 --> 00:02:41,850 in which case that message 71 00:02:41,850 --> 00:02:43,353 doesn't exist anymore. 72 00:02:44,310 --> 00:02:45,360 And then we've got a problem, 73 00:02:45,360 --> 00:02:46,560 because five seconds later, 74 00:02:46,560 --> 00:02:48,960 we're gonna try to access the message again 75 00:02:48,960 --> 00:02:50,340 and it's gone. 76 00:02:50,340 --> 00:02:53,610 So in that case, if you have spawn in the thread, 77 00:02:53,610 --> 00:02:55,260 is the classic example. 78 00:02:55,260 --> 00:02:57,420 If the lambda or the closure 79 00:02:57,420 --> 00:02:59,505 doesn't apparently need to do a move, 80 00:02:59,505 --> 00:03:02,095 then you have to forcibly do a move. 81 00:03:02,095 --> 00:03:03,030 Okay? 82 00:03:03,030 --> 00:03:04,230 Basically what will happen is, 83 00:03:04,230 --> 00:03:06,340 you will move this object 84 00:03:07,440 --> 00:03:10,470 into ownership of the closure, 85 00:03:10,470 --> 00:03:12,758 so that that object will then live. 86 00:03:12,758 --> 00:03:14,910 It'll belong to the closure 87 00:03:14,910 --> 00:03:16,620 and will live as long as the closure, 88 00:03:16,620 --> 00:03:18,870 it doesn't disappear halfway through. 89 00:03:18,870 --> 00:03:21,824 So this is called forcible movement. 90 00:03:21,824 --> 00:03:23,763 Let's see an example of that. 91 00:03:24,660 --> 00:03:26,700 So capturing a value forcibly. 92 00:03:26,700 --> 00:03:28,720 Here's my external string 93 00:03:28,720 --> 00:03:31,080 and this is the start of my method. 94 00:03:31,080 --> 00:03:31,913 And by the way, 95 00:03:31,913 --> 00:03:33,630 there's the end of my method. 96 00:03:33,630 --> 00:03:37,950 And here I run, I spawn another thread. 97 00:03:37,950 --> 00:03:39,360 We're gonna look at multi-threading 98 00:03:39,360 --> 00:03:41,130 in more detail later on 99 00:03:41,130 --> 00:03:41,970 in the video series, 100 00:03:41,970 --> 00:03:44,970 but this was kind of relevant here. 101 00:03:44,970 --> 00:03:48,090 We must forcibly move any variables 102 00:03:48,090 --> 00:03:50,460 that we mention here, like message. 103 00:03:50,460 --> 00:03:53,243 We must forcibly move them into the closure, 104 00:03:53,243 --> 00:03:55,590 otherwise it won't compile. 105 00:03:55,590 --> 00:03:57,780 So the closure object will basically 106 00:03:57,780 --> 00:04:00,750 seize ownership of the message, 107 00:04:00,750 --> 00:04:02,520 and that message will live as long 108 00:04:02,520 --> 00:04:03,510 as the closure lives, 109 00:04:03,510 --> 00:04:05,850 which is gonna be five seconds. 110 00:04:05,850 --> 00:04:06,683 Okay? 111 00:04:06,683 --> 00:04:11,670 So it'll print "Hello", and there it is. 112 00:04:11,670 --> 00:04:13,590 It'll wait five seconds, 113 00:04:13,590 --> 00:04:17,070 and then it'll print "Hello" again. 114 00:04:17,070 --> 00:04:19,500 And let's see it in practice. 115 00:04:19,500 --> 00:04:20,333 I just wanna make sure 116 00:04:20,333 --> 00:04:23,280 that I've called that function from the top. 117 00:04:23,280 --> 00:04:24,579 So from my top level function, 118 00:04:24,579 --> 00:04:26,730 capture value forcibly. 119 00:04:26,730 --> 00:04:27,563 Oh, by the way, 120 00:04:27,563 --> 00:04:30,390 notice my ten second delay to give the example 121 00:04:30,390 --> 00:04:32,580 enough time to run through the completion. 122 00:04:32,580 --> 00:04:36,453 So let's see what gets printed. 123 00:04:40,080 --> 00:04:41,340 I'm just gonna wait for the example, 124 00:04:41,340 --> 00:04:43,890 you can see, there's a weight going on here. 125 00:04:43,890 --> 00:04:45,600 And this, this is the ten second wait, 126 00:04:45,600 --> 00:04:46,950 it's finishing. 127 00:04:46,950 --> 00:04:50,783 So it said start of method 128 00:04:50,783 --> 00:04:52,897 and start the method. 129 00:04:52,897 --> 00:04:57,420 And then this thread got spawned. 130 00:04:57,420 --> 00:04:58,920 And when you spawn a thread, 131 00:04:58,920 --> 00:05:00,180 it takes, you know, 132 00:05:00,180 --> 00:05:03,001 it takes a few milliseconds to get going. 133 00:05:03,001 --> 00:05:05,820 So before it could actually print anything. 134 00:05:05,820 --> 00:05:08,280 The main thread, there were two threads running now. 135 00:05:08,280 --> 00:05:11,190 The main thread and the thread 136 00:05:11,190 --> 00:05:13,650 that's running the closure. 137 00:05:13,650 --> 00:05:18,650 The main thread continued and said end of method. 138 00:05:18,650 --> 00:05:21,188 So this function has actually returned 139 00:05:21,188 --> 00:05:23,133 back up above. 140 00:05:24,270 --> 00:05:25,380 Okay, so the main thread 141 00:05:25,380 --> 00:05:26,400 is now basically waiting 142 00:05:26,400 --> 00:05:27,693 on a ten second wait. 143 00:05:29,540 --> 00:05:32,430 So this function is already terminated, 144 00:05:32,430 --> 00:05:35,198 and if this message had just been stack based, 145 00:05:35,198 --> 00:05:37,290 we'd have lost it, 146 00:05:37,290 --> 00:05:38,730 which is a shame because we needed 147 00:05:38,730 --> 00:05:40,560 to access it here. 148 00:05:40,560 --> 00:05:42,390 So as soon as this closure started, 149 00:05:42,390 --> 00:05:46,470 it immediately moved this message into the closure. 150 00:05:46,470 --> 00:05:48,120 So it lived long enough. 151 00:05:48,120 --> 00:05:49,410 This closure, which is really 152 00:05:49,410 --> 00:05:53,640 an object in disguise now owns the message. 153 00:05:53,640 --> 00:05:55,380 Ownership has been moved into the closure, 154 00:05:55,380 --> 00:05:56,730 which means we can print it 155 00:05:58,290 --> 00:06:00,270 Start with message closure, hello. 156 00:06:00,270 --> 00:06:02,310 And then it waited five seconds, 157 00:06:02,310 --> 00:06:05,456 and then message at the end of closure. 158 00:06:05,456 --> 00:06:06,573 Hello. 159 00:06:07,530 --> 00:06:12,090 So when you have a closure that really, 160 00:06:12,090 --> 00:06:13,710 well, to put it another way, 161 00:06:13,710 --> 00:06:15,030 when you're spawning a thread 162 00:06:15,030 --> 00:06:17,040 that accesses external variables, 163 00:06:17,040 --> 00:06:18,383 you must say move. 164 00:06:18,383 --> 00:06:19,900 You must tell the compiler, 165 00:06:19,900 --> 00:06:21,660 you must advise the compiler 166 00:06:21,660 --> 00:06:24,150 that a movement of state is necessary. 167 00:06:24,150 --> 00:06:26,710 Interestingly, if you forgot to say move, 168 00:06:26,710 --> 00:06:30,000 you'd actually get a compiler error, okay? 169 00:06:30,000 --> 00:06:31,740 Which may be not what you expected. 170 00:06:31,740 --> 00:06:34,293 Let's do a cargo check or cargo run. 171 00:06:35,430 --> 00:06:38,069 So the error message, oof, 172 00:06:38,069 --> 00:06:40,110 it's quite lengthy. 173 00:06:40,110 --> 00:06:42,217 It's saying my thread, 174 00:06:42,217 --> 00:06:44,733 which is capturing message, 175 00:06:46,080 --> 00:06:49,950 the printing macro only needs to borrow a reference. 176 00:06:49,950 --> 00:06:52,350 And it's saying, if you're borrowing a reference, 177 00:06:52,350 --> 00:06:54,330 then the thing that you're referring to, 178 00:06:54,330 --> 00:06:57,090 message, might have disappeared. 179 00:06:57,090 --> 00:06:59,700 The closure might outlive the message. 180 00:06:59,700 --> 00:07:02,940 So it's basically saying you've got to basically 181 00:07:02,940 --> 00:07:06,720 either declare it a static or use the move keyword, 182 00:07:06,720 --> 00:07:08,558 saying you should use the move keyword 183 00:07:08,558 --> 00:07:11,792 to forcibly move ownership of message 184 00:07:11,792 --> 00:07:13,620 into the closure. 185 00:07:13,620 --> 00:07:15,240 So that it will definitely live 186 00:07:15,240 --> 00:07:17,460 as long as the closure lives. 187 00:07:17,460 --> 00:07:20,490 Okay, so I've put the move keyword back in, 188 00:07:20,490 --> 00:07:22,500 could then run the example 189 00:07:22,500 --> 00:07:25,110 and it's working again. 190 00:07:25,110 --> 00:07:27,270 So in this section, 191 00:07:27,270 --> 00:07:30,570 we've discussed how to move a value into a closure. 192 00:07:30,570 --> 00:07:32,310 We've looked at two scenarios. 193 00:07:32,310 --> 00:07:34,767 One where we call the drop function, 194 00:07:34,767 --> 00:07:36,237 where the compiler can see 195 00:07:36,237 --> 00:07:39,150 that the closure requires ownership, 196 00:07:39,150 --> 00:07:41,040 and it will move ownership of the variable 197 00:07:41,040 --> 00:07:42,420 into the closure, 198 00:07:42,420 --> 00:07:44,700 which means you can't then use it afterwards 199 00:07:44,700 --> 00:07:46,170 in the external code. 200 00:07:46,170 --> 00:07:47,640 And the second example we looked at 201 00:07:47,640 --> 00:07:49,320 with spawning threads, 202 00:07:49,320 --> 00:07:52,169 is where we know that we require 203 00:07:52,169 --> 00:07:53,970 a movement of ownership 204 00:07:53,970 --> 00:07:55,890 so that the variables live long enough 205 00:07:55,890 --> 00:07:56,900 for the thread to complete. 206 00:07:56,900 --> 00:07:57,930 In that case, 207 00:07:57,930 --> 00:07:59,340 you have to say move 208 00:07:59,340 --> 00:08:00,804 to make sure the compiler doesn't move 209 00:08:00,804 --> 00:08:02,733 instead of just doing a borrow.