The Rust Programming Language Ownership Recap & Memory Exercise
Transcript from the "Ownership Recap & Memory Exercise" Lesson
>> So to review, we first talked about manual memory management. So what you're explicitly calling dealloc like you wouldn't see. And now the downsides of this are you can get use after freeze, you can get double freeze. It's a potential source of security problems. It's really just like something that absolutely does work, but it's pretty error prone and the stakes can be pretty high.
[00:00:19] These are really nasty bugs. We don't wanna deal with them. We really wanna have automatic memory management. But, we talked about that automatic memory management and garbage collected languages, you have to deal with garbage collection pauses. You have to deal with the overhead of the garbage collector itself.
[00:00:33] Rust has these instead, it has these pretty simple scope-based heuristics. But they do have a number of edge cases which we're starting to get into now. But it is a way to do automatic memory management without the overhead of a GC. And this is exactly how Rust is able to be competitive with C and C++ in terms of performance cuz they don't have garbage collectors either.
[00:01:08] That can happen when the compiler says, if I were to follow my heuristics, I would accidentally give you a use after free or a double free. I don't wanna do that to you, so therefore, instead I'm gonna give you an error. And I'm gonna to ask you to rearrange your program in a way such that the heuristics will no longer cause a problem.
[00:01:24] We talked about ownership, which is the first of these sort of three concepts that all have to do with this automatic memory management, the next two being borrowing and then lifetimes. And this is basically where we see our first category of compiler errors that are designed to prevent use-after-free errors.
[00:01:38] Finally, we talked about cloning, which is a beginner's best friend and Rust. It's a way that you can temporarily sacrifice some performance to just sort of get past these compiler errors that are gonna be unfamiliar at first. So, as before, we have our main here. And this, like I mentioned previously is the exercise where we're going to basically encounter a use-after-move compiler error.
[00:01:57] And we're gonna solve it in two different ways. So basically, we've got a whole bunch of numbers here. We've got some of nums, which calls the sum function. And we got here looking at some basically some takes a vec of i64s, and it just goes through and iterates over the numbers and then just adds each of them to total.
[00:02:15] We also have product which is the same thing except that it multiplies instead of adding. And finally, we have average which basically takes the length of the numbers and then divides the sum of the numbers by the length, so sum, product and average. And all we're trying to do is, we're trying to do something that would be in most languages pretty straightforward, which is called sum of the numbers.
[00:02:31] Then call products on the same numbers and then call average on the same numbers. And as you might guess, this is going to result in the same type of error that we saw on the slides, namely s use-after-move error. Because when we pass numbers to each of these things, it gets moved into there and then those scopes take ownership of them.
[00:02:47] And then automatically deallocate them because they're not returning it. So we're gonna use the two techniques we talked about. One is just calling .clone on numbers, which is the quick and easy way. But I encourage you to try out the extra technique of having these functions return a tuple.
[00:03:04] So not just the i64 but also return this vec again, so that the caller can regain access to it and have it get moved back into the caller scope. And therefore, prevent the compiler from trying to deallocate it. And either of those are ways that you can address the use-after-move.