1 00:00:06,570 --> 00:00:07,860 - Memory management in Rust 2 00:00:07,860 --> 00:00:09,690 is quite different from other languages. 3 00:00:09,690 --> 00:00:10,770 It's the one thing 4 00:00:10,770 --> 00:00:14,460 that differentiates it from C++ for example. 5 00:00:14,460 --> 00:00:15,293 In this lesson, 6 00:00:15,293 --> 00:00:17,400 we're going to look at issues such as scope. 7 00:00:17,400 --> 00:00:18,990 What is the scope of a variable? 8 00:00:18,990 --> 00:00:20,670 In other words, where can you see it? 9 00:00:20,670 --> 00:00:22,020 Where is it visible? 10 00:00:22,020 --> 00:00:24,060 Where physically is memory allocated? 11 00:00:24,060 --> 00:00:25,260 When you allocate an object, 12 00:00:25,260 --> 00:00:26,970 is it allocated on the stack, 13 00:00:26,970 --> 00:00:29,370 on the heap, or somewhere else? 14 00:00:29,370 --> 00:00:31,500 And we'll also talk about ownership. 15 00:00:31,500 --> 00:00:33,660 This is a big issue in Rust. 16 00:00:33,660 --> 00:00:36,120 When you assign one variable to another, 17 00:00:36,120 --> 00:00:37,410 you have to worry or think 18 00:00:37,410 --> 00:00:41,040 about who actually owns the object after the assignment. 19 00:00:41,040 --> 00:00:44,610 Rust ownership and borrowing is quite tricky. 20 00:00:44,610 --> 00:00:46,920 So we're gonna take it carefully and step by step 21 00:00:46,920 --> 00:00:49,140 so that you can understand exactly 22 00:00:49,140 --> 00:00:51,150 how it works, and why it works, 23 00:00:51,150 --> 00:00:55,410 and why it makes your programs more robust and safer. 24 00:00:55,410 --> 00:00:57,300 So first of all, local scope. 25 00:00:57,300 --> 00:00:59,940 When you define a variable inside a function, 26 00:00:59,940 --> 00:01:01,800 it has local scope. 27 00:01:01,800 --> 00:01:04,290 It goes out of scope at the end of the function. 28 00:01:04,290 --> 00:01:05,610 At the end of the curly brackets, 29 00:01:05,610 --> 00:01:07,890 the variable disappears and it's gone. 30 00:01:07,890 --> 00:01:09,390 Here's a simple example. 31 00:01:09,390 --> 00:01:11,490 I've declared a variable x inside a function. 32 00:01:11,490 --> 00:01:13,770 I've given it the value 42 obviously, 33 00:01:13,770 --> 00:01:15,330 and the variable goes out of scope 34 00:01:15,330 --> 00:01:17,820 at the end of the function and it disappears. 35 00:01:17,820 --> 00:01:19,290 So it's fairly straightforward. 36 00:01:19,290 --> 00:01:21,030 Most languages had worked this way. 37 00:01:21,030 --> 00:01:24,120 This is the way most variables are declared in Rust, 38 00:01:24,120 --> 00:01:27,090 as local variables on the stack, 39 00:01:27,090 --> 00:01:29,850 and they disappear at the end of the function. 40 00:01:29,850 --> 00:01:32,370 You can also declare a variable inside a block. 41 00:01:32,370 --> 00:01:34,530 For example, inside an if statement. 42 00:01:34,530 --> 00:01:36,720 When you define a variable in a block, 43 00:01:36,720 --> 00:01:38,940 it has block scope and it goes out of scope 44 00:01:38,940 --> 00:01:41,070 at the end of the closing curly brackets. 45 00:01:41,070 --> 00:01:42,990 So let's have a look at this example. 46 00:01:42,990 --> 00:01:45,060 Here, I've got a test. 47 00:01:45,060 --> 00:01:46,110 And inside the test, 48 00:01:46,110 --> 00:01:48,900 I declare a block scope variable s1. 49 00:01:48,900 --> 00:01:51,120 s1 is only visible from here 50 00:01:51,120 --> 00:01:52,890 to the end of the enclosing block, 51 00:01:52,890 --> 00:01:54,870 and it goes out of scope at this point 52 00:01:54,870 --> 00:01:57,240 and you can't access it down here. 53 00:01:57,240 --> 00:02:00,000 Okay, so Rust has proper block scope. 54 00:02:00,000 --> 00:02:01,170 Again, that's not a surprise. 55 00:02:01,170 --> 00:02:04,290 Most languages also work like this. 56 00:02:04,290 --> 00:02:06,150 Where is memory allocated? 57 00:02:06,150 --> 00:02:08,250 Well, when you declare available in the function, 58 00:02:08,250 --> 00:02:11,280 by default it's allocated on the stack. 59 00:02:11,280 --> 00:02:14,760 So when you enter a function, the variable gets created. 60 00:02:14,760 --> 00:02:17,340 When you exit the function, the variable gets popped 61 00:02:17,340 --> 00:02:19,440 off the stack at the end of the function 62 00:02:19,440 --> 00:02:21,750 and it disappears automatically, okay? 63 00:02:21,750 --> 00:02:26,010 So it's possible to allocate memory elsewhere rather 64 00:02:26,010 --> 00:02:28,050 than allocating on the stack. 65 00:02:28,050 --> 00:02:30,660 You can also allocate a static storage. 66 00:02:30,660 --> 00:02:34,080 So for example, a global variable would have static storage. 67 00:02:34,080 --> 00:02:36,480 It exists forever, basically until the end 68 00:02:36,480 --> 00:02:38,220 of the entire program. 69 00:02:38,220 --> 00:02:41,310 It's also possible to declare a variable 70 00:02:41,310 --> 00:02:43,590 inside a function, a static storage. 71 00:02:43,590 --> 00:02:46,020 And you can do this in C and C++ as well. 72 00:02:46,020 --> 00:02:48,960 When you enter a function, the variable gets created 73 00:02:48,960 --> 00:02:52,050 and then it lives forever across function calls, 74 00:02:52,050 --> 00:02:55,800 it's statically allocated and can retain a value 75 00:02:55,800 --> 00:02:57,060 each time you call it, 76 00:02:57,060 --> 00:03:00,150 maybe you can increment the value of a static counter. 77 00:03:00,150 --> 00:03:04,740 So it has a static permanently allocated storage class. 78 00:03:04,740 --> 00:03:09,390 That too will be quite tricky in Rust as we'll see later. 79 00:03:09,390 --> 00:03:12,090 You can also allocate objects on the heap. 80 00:03:12,090 --> 00:03:15,960 This would be equivalent to using new and delete in C++. 81 00:03:15,960 --> 00:03:19,620 So in Rust, this would be the exception to the rule, 82 00:03:19,620 --> 00:03:22,380 allocating on the heap, you tend not to. 83 00:03:22,380 --> 00:03:26,070 If you want to, then you use a structure called box. 84 00:03:26,070 --> 00:03:28,170 A box is effectively a pointer 85 00:03:28,170 --> 00:03:30,570 to an object in dynamic memory. 86 00:03:30,570 --> 00:03:34,800 The box contains the address of the object on the heap. 87 00:03:34,800 --> 00:03:37,140 So we'll have a look at that later as well. 88 00:03:37,140 --> 00:03:38,460 So let's have a look at an example. 89 00:03:38,460 --> 00:03:40,290 First of all, of local scope. 90 00:03:40,290 --> 00:03:43,860 It's in the lesson six scope ownership project. 91 00:03:43,860 --> 00:03:46,350 I created this project using cargo. 92 00:03:46,350 --> 00:03:47,910 And in particular, 93 00:03:47,910 --> 00:03:51,210 each section in this lesson will be a different file. 94 00:03:51,210 --> 00:03:53,160 So for this section, 95 00:03:53,160 --> 00:03:56,917 the file we're gonna look at is demo_locals.rs. 96 00:03:56,917 --> 00:03:59,700 So we look at the code and then we'll run it as normal. 97 00:03:59,700 --> 00:04:00,990 So first of all, 98 00:04:00,990 --> 00:04:05,550 let me just show you the structure of the project. 99 00:04:05,550 --> 00:04:09,150 So if you downloaded the demos for the course, 100 00:04:09,150 --> 00:04:12,630 go into rustdev, lesson06_scope_ownership. 101 00:04:12,630 --> 00:04:15,660 All the demos for this lesson are in here. 102 00:04:15,660 --> 00:04:16,920 So let's open that project 103 00:04:16,920 --> 00:04:19,710 in Visual Studio Code and take a look at the code. 104 00:04:19,710 --> 00:04:20,700 Okay, so here it is. 105 00:04:20,700 --> 00:04:24,090 Now what I've done from now on with my demos 106 00:04:24,090 --> 00:04:26,550 is I've organized my code into separate files just 107 00:04:26,550 --> 00:04:28,590 to give me a little bit more structure. 108 00:04:28,590 --> 00:04:30,960 So for each part of the lesson, 109 00:04:30,960 --> 00:04:33,300 for each separate kind of section, 110 00:04:33,300 --> 00:04:36,150 we'll have a different file, okay? 111 00:04:36,150 --> 00:04:40,533 So at the moment, we're going to be looking at demo_locals. 112 00:04:40,533 --> 00:04:41,640 Let's have a look at that first of all. 113 00:04:41,640 --> 00:04:42,990 I've defined a function. 114 00:04:42,990 --> 00:04:45,120 So demo_locals is a file, 115 00:04:45,120 --> 00:04:47,820 but in Rust you'd call it a module, okay? 116 00:04:47,820 --> 00:04:50,100 So it's a module, which is like a namespace. 117 00:04:50,100 --> 00:04:54,150 Remember, a module is like a namespace in Rust. 118 00:04:54,150 --> 00:04:57,840 And by default when you define functions and structures 119 00:04:57,840 --> 00:05:00,453 and so on, when you define them in a module, 120 00:05:01,920 --> 00:05:03,720 it would be private to the module 121 00:05:03,720 --> 00:05:06,750 unless you say pub, pub means public. 122 00:05:06,750 --> 00:05:08,070 And if you didn't say pub, 123 00:05:08,070 --> 00:05:10,530 then it would be private to this file, 124 00:05:10,530 --> 00:05:12,900 means you could only call the function from this file. 125 00:05:12,900 --> 00:05:17,160 So Rust tries to encourage privacy wherever possible. 126 00:05:17,160 --> 00:05:19,230 If you want to be able to call this function elsewhere 127 00:05:19,230 --> 00:05:21,600 like I'm gonna call it from main in a moment, 128 00:05:21,600 --> 00:05:24,690 then you have to declare this public. 129 00:05:24,690 --> 00:05:25,920 So it's a public function. 130 00:05:25,920 --> 00:05:27,480 I've got a a print statement here just 131 00:05:27,480 --> 00:05:29,553 to introduce the example. 132 00:05:30,630 --> 00:05:32,220 The terminology here. 133 00:05:32,220 --> 00:05:33,990 Demo, I mean, this is just a print statement 134 00:05:33,990 --> 00:05:36,720 but demo_locals is the module name, 135 00:05:36,720 --> 00:05:38,550 which is like a namespace. 136 00:05:38,550 --> 00:05:41,850 And in that module or in that namespace, 137 00:05:41,850 --> 00:05:43,680 we have the do_it function. 138 00:05:43,680 --> 00:05:46,563 It's the do_it function to demonstrate local variables. 139 00:05:47,631 --> 00:05:50,133 I declared a local variable x, 42. 140 00:05:50,970 --> 00:05:52,800 x is non-zero, clearly. 141 00:05:52,800 --> 00:05:56,690 So it'll come into the if block and it'll create s1. 142 00:05:56,690 --> 00:05:58,650 s1 is a block scope variable. 143 00:05:58,650 --> 00:06:00,990 It'll go out of scope at the end of the block. 144 00:06:00,990 --> 00:06:03,150 Okay, so that's the end of s1. 145 00:06:03,150 --> 00:06:05,880 I can't access s1 down here obviously, 146 00:06:05,880 --> 00:06:08,820 because it's disappeared out of scope at that point. 147 00:06:08,820 --> 00:06:10,800 Okay, so I'd get a compile error 148 00:06:10,800 --> 00:06:14,430 if I tried to access s1 out of scope. 149 00:06:14,430 --> 00:06:15,990 Okay, that's simple enough really. 150 00:06:15,990 --> 00:06:16,823 So let's have a look 151 00:06:16,823 --> 00:06:19,920 at where we call this function from main. 152 00:06:19,920 --> 00:06:22,170 So in main, I've got a couple 153 00:06:22,170 --> 00:06:24,000 of things I want to talk about here. 154 00:06:24,000 --> 00:06:26,160 Here's my main function. 155 00:06:26,160 --> 00:06:29,880 I'm gonna un-comment the call, like so. 156 00:06:29,880 --> 00:06:32,330 So from the top, let me just remind you 157 00:06:32,330 --> 00:06:34,230 of a few things we've already discussed. 158 00:06:34,230 --> 00:06:36,210 Let me just remind you about these. 159 00:06:36,210 --> 00:06:40,170 This syntax, when you say hash square brackets, 160 00:06:40,170 --> 00:06:42,630 that's called an attribute in Rust. 161 00:06:42,630 --> 00:06:45,510 And it's effectively kind of like a code directive, 162 00:06:45,510 --> 00:06:47,670 like a compiler directive. 163 00:06:47,670 --> 00:06:50,940 And the allowed dead code means 164 00:06:50,940 --> 00:06:54,450 if I have functions which aren't being invoked 165 00:06:54,450 --> 00:06:56,670 or variables that aren't being used, 166 00:06:56,670 --> 00:06:59,100 then normally I'd get a warning from the compiler 167 00:06:59,100 --> 00:07:01,980 to say why have you not called this function? 168 00:07:01,980 --> 00:07:03,810 If you say allow dead code, 169 00:07:03,810 --> 00:07:05,760 then that allows you to have dead code. 170 00:07:05,760 --> 00:07:07,350 You don't get warnings 171 00:07:07,350 --> 00:07:10,290 if there are functions or whatever that you haven't used. 172 00:07:10,290 --> 00:07:14,820 So in my demo, it'll hide or suppress the compiler warnings. 173 00:07:14,820 --> 00:07:17,220 The exclamation mark here means 174 00:07:17,220 --> 00:07:20,760 that this attribute applies for the entire crate. 175 00:07:20,760 --> 00:07:23,760 And remember in Rust terminology, 176 00:07:23,760 --> 00:07:27,420 the entire project is a crate, okay? 177 00:07:27,420 --> 00:07:29,850 My project is a crate. 178 00:07:29,850 --> 00:07:34,110 And if you say hash exclamation mark 179 00:07:34,110 --> 00:07:37,050 then this attribute applies for the entire crate. 180 00:07:37,050 --> 00:07:38,100 It's the exclamation mark 181 00:07:38,100 --> 00:07:40,500 that makes it a crate level attribute. 182 00:07:40,500 --> 00:07:45,500 So basically allow dead code throughout my entire crate. 183 00:07:45,960 --> 00:07:49,680 And also allow unused imports. 184 00:07:49,680 --> 00:07:54,180 An import is a use statement, that's technically an import. 185 00:07:54,180 --> 00:07:56,340 It's where you are bringing some symbol 186 00:07:56,340 --> 00:07:58,770 into the current scope. 187 00:07:58,770 --> 00:08:00,510 We'll look at this variable later. 188 00:08:00,510 --> 00:08:03,030 Global message will be useful later on. 189 00:08:03,030 --> 00:08:05,070 We can ignore it for now. 190 00:08:05,070 --> 00:08:09,510 But, because I've introduced this variable name into scope, 191 00:08:09,510 --> 00:08:12,360 if I hadn't actually made use of it anywhere in my code, 192 00:08:12,360 --> 00:08:16,230 I'd get a warning to say, "You haven't used this variable." 193 00:08:16,230 --> 00:08:19,890 I'm going to allow unused imports, okay? 194 00:08:19,890 --> 00:08:22,020 So this use statement 195 00:08:22,020 --> 00:08:25,290 where I've imported this name into scope, 196 00:08:25,290 --> 00:08:30,290 allow unused imports, okay, across the entire crate. 197 00:08:31,230 --> 00:08:33,150 So those attributes are going 198 00:08:33,150 --> 00:08:35,550 to stop me getting warnings everywhere, 199 00:08:35,550 --> 00:08:37,050 which is quite nice, really. 200 00:08:37,050 --> 00:08:40,320 And then, so let me remind you about the mod keyword. 201 00:08:40,320 --> 00:08:42,090 So technically what the mod keyword does 202 00:08:42,090 --> 00:08:44,190 is it declares a module. 203 00:08:44,190 --> 00:08:47,010 It introduces a module or a namespace. 204 00:08:47,010 --> 00:08:49,860 And if you have a semicolon directly 205 00:08:49,860 --> 00:08:52,903 after the module declaration, then what Rust will do is 206 00:08:52,903 --> 00:08:57,210 it'll look for a file called demo_locals.rs 207 00:08:57,210 --> 00:09:01,290 and basically drag that code into your application. 208 00:09:01,290 --> 00:09:04,260 So here is demo_locals. 209 00:09:04,260 --> 00:09:05,760 We looked at that file earlier. 210 00:09:05,760 --> 00:09:10,620 Just having the file in your project isn't enough, okay? 211 00:09:10,620 --> 00:09:12,630 You also have to kind of introduce it 212 00:09:12,630 --> 00:09:14,550 into your application crate. 213 00:09:14,550 --> 00:09:16,260 So just the existence of the file, 214 00:09:16,260 --> 00:09:17,910 if you just left it like that 215 00:09:17,910 --> 00:09:19,810 and you didn't have the mod statement, 216 00:09:21,270 --> 00:09:25,470 then it's as if this file will never get imported, okay? 217 00:09:25,470 --> 00:09:28,080 It's as if it was just not there. 218 00:09:28,080 --> 00:09:32,250 So just defined in the file is only half of the equation. 219 00:09:32,250 --> 00:09:34,680 You've also gotta declare the module. 220 00:09:34,680 --> 00:09:39,600 So technically, the main file, main.rs, 221 00:09:39,600 --> 00:09:42,720 declares a module called demo_locals, 222 00:09:42,720 --> 00:09:46,740 causes the compiler to import that file, okay? 223 00:09:46,740 --> 00:09:50,310 So we now have a public function called do_it, 224 00:09:50,310 --> 00:09:53,220 which is in the demo_locals module, 225 00:09:53,220 --> 00:09:58,220 demo_locals::do_it is now available to us. 226 00:09:58,260 --> 00:10:00,360 And that's good because I call it down here. 227 00:10:00,360 --> 00:10:04,350 So in my main code, I call from the demo_locals module, 228 00:10:04,350 --> 00:10:08,280 like a namespace, invoke the do_it function, very nice. 229 00:10:08,280 --> 00:10:10,860 So here is my do_it function in demo_locals, 230 00:10:10,860 --> 00:10:12,753 it's gonna run that code. 231 00:10:14,010 --> 00:10:16,620 Okay, all fine, good. 232 00:10:16,620 --> 00:10:18,420 So let's actually run it then. 233 00:10:18,420 --> 00:10:21,240 When you run the application, it runs the main function. 234 00:10:21,240 --> 00:10:23,670 It looks for a file called main.rs 235 00:10:23,670 --> 00:10:25,770 and it runs the main function in there. 236 00:10:25,770 --> 00:10:27,390 It doesn't matter how many files you've got, 237 00:10:27,390 --> 00:10:30,663 it's the main.rs is the entry point always. 238 00:10:32,370 --> 00:10:33,690 Okay, and we'll call that function. 239 00:10:33,690 --> 00:10:35,763 So let's do a cargo run. 240 00:10:37,980 --> 00:10:39,630 So we're not gonna get any warnings here, 241 00:10:39,630 --> 00:10:43,080 because of our crate scope attributes 242 00:10:43,080 --> 00:10:45,303 that suppresses warnings like so. 243 00:10:46,170 --> 00:10:50,610 It came into my demo_locals do_it function, which is great. 244 00:10:50,610 --> 00:10:52,050 That's the call. 245 00:10:52,050 --> 00:10:57,050 And in that function, it declares s1 and prints s1. 246 00:10:57,180 --> 00:10:58,860 So that's handy. 247 00:10:58,860 --> 00:11:00,060 And that's the end of that. 248 00:11:00,060 --> 00:11:03,600 And if I accidentally try to access s1 here, 249 00:11:03,600 --> 00:11:05,280 I'd expect to get a compiler error, 250 00:11:05,280 --> 00:11:09,780 because s1 goes out of scope and is no longer visible here. 251 00:11:09,780 --> 00:11:12,570 So what I'll do, instead of doing a cargo run, 252 00:11:12,570 --> 00:11:14,610 I'll just do a cargo check. 253 00:11:14,610 --> 00:11:17,340 Remember cargo check, it just does a compile 254 00:11:17,340 --> 00:11:20,310 to check the syntax without actually generating an exe. 255 00:11:20,310 --> 00:11:22,530 So this will give you the compiler errors. 256 00:11:22,530 --> 00:11:25,743 I'm expecting a compiler error on line 13, unlucky for some. 257 00:11:26,940 --> 00:11:31,940 Okay, so in my project lesson06, ownership, 258 00:11:33,870 --> 00:11:38,870 it's complaining in demo_locals line 13. 259 00:11:39,180 --> 00:11:42,510 It's complaining that s1 is not found in scope. 260 00:11:42,510 --> 00:11:43,620 Yes, indeed. 261 00:11:43,620 --> 00:11:48,620 So let's fix that and just check the syntax again. 262 00:11:50,280 --> 00:11:51,450 Fantastic, okay? 263 00:11:51,450 --> 00:11:53,700 So that's a quick introduction to scope. 264 00:11:53,700 --> 00:11:55,830 It leads us into the rest of the lesson 265 00:11:55,830 --> 00:11:58,443 where we can talk about ownership in more detail.