Check out a free preview of the full The Rust Programming Language course
The "Rust Memory Management" Lesson is part of the full, The Rust Programming Language course featured in this preview video. Here's what you'd learn in this lesson:
Richard discusses how automatic memory management works in Rust, briefly mentions edge cases where automatic deallocation could error, and how functions can be rewritten to safely deallocate memory sooner. Rust will automatically insert the dealloc function when the data is no longer in scope and it's no longer being used.
Transcript from the "Rust Memory Management" Lesson
[00:00:00]
>> So how does Rust do it? We've seen in all these amas we've done so far Rust has never asked us to deallocate memory manually. We've used vec. We've used strings, both of those are dynamic length, and there was no problem. Rust just took care of that for us somehow.
[00:00:14]
And yet, Rust doesn't have a garbage collector. So how does it do it, how does it avoid this thing that we have to do in C of manual memory. How does Rust do automatic memory management without a garbage collector? Here's how we do it. Okay, so back here we saw that vec was causing allocation basically rust just says you know what I'm gonna automatically call dealloc on orders right before we return the function.
[00:00:40]
Because it's no longer in use, [SOUND] the end. It basically just inserts the same instruction that C would have done that you would have had to do manually in C and it inserts it in the correct place. That we're seeing it that way, [LAUGH]. One of the previous times I was doing this course, someone really said why doesn't everyone do it that way?
[00:01:00]
It's a totally valid question, I mean it seems like yeah, allocate it when you need it and then, deallocate it when you don't need it anymore, right? Like duh, and yeah, it does raise the question of why not do this, and, when I put it out in a simple example like this, it doesn't really seem like there are a lot of edge cases.
[00:01:17]
But it turns out there are some important edge cases here. And this gets into the concepts of ownership and borrowing and lifetimes that are sort of unique to the rust compiler. They're basically around dealing with the edge cases. Where this relatively conceptually simple system runs into potential problems and how it solves them.
[00:01:35]
Okay, so basically rusts kind of baseline algorithm for when to deallocate something is just says when it goes out of scope, like okay. Orders is out of scope here. So from now on, I know that nobody could possibly access orders because it was out of scope and scope is how you determine access for things.
[00:01:55]
So yeah, we created it up here and then over here when the function returns it's no longer in scope, I know that nobody can access it so it's definitely safe to deallocate it. Now, when it does it this in this way, rust is able to guarantee that there is not gonna be any possibility of a use after free because for use after free to happen, somebody would have to actually use it.
[00:02:14]
But once it's out of scope, there's no possibility that anyone can use it. And also there's a guarantee of no double free because it only goes out of scope once. So there's only one place where it says I'm gonna call the deallocation. Great, also, of course, there's a guarantee of no garbage collection pause because there just is no garbage collector.
[00:02:33]
Okay, so, we saw that this is like sort of when it chose to free the memory, but it's worth noting that Rust could have safely chosen an earlier time to free the memory. Like it could have freed it before final orders right? After this loop had run this is the last place that actually gets us.
[00:02:51]
So yeah, it could have just very easily done the deallocate up here. But again, Rust has this pretty simple heuristic of free it when it goes out of scope so this is not where it would choose to do that. Now, is that gonna matter probably not in practice, but let's say that it does for some reason, let's say that you really want to have that memory reclaimed and marked as free before you call this finish function.
[00:03:11]
Because finish is really memory hungry or something like that needs absolutely every resource. Well, if you wanna do that, you can just rewrite your function like this. So here we have exactly the same business logic. We start with total orders, zero, we define orders as the same vec, we iterate over the same way, we add up total orders, then we returned finish of total orders.
[00:03:31]
The only difference is I added these sort of standalone curly braces. So this is something you can do in Rust, you can just sort of define an anonymous scope like this. And this is kind of what it's used for is basically as a way to give Rust a hint about how you want to manage the memory.
[00:03:46]
So now because orders is going out of scope here before the end of the function that's going to result in rust choosing to deallocate it a little bit sooner. So the only difference between this version and the previous one is when Rust is going to choose to automatically deallocate orders.
[00:04:00]
In practice, this is something that you don't tend to do very often or it doesn't tend to be important very often, but it's worth noting that you can use this to get sort of a similar level of control as what you would get in something like C or C++.
[00:04:12]
Obviously not the same level of control because you're not actually doing manual memory management. But it's a way that you can do it, while still maintaining these really strong guarantees of like, yeah, no chance of a use after free, no chance of a double free and still no need for garbage collector.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops