Rust for TypeScript Developers Borrow Checker Practice
Transcript from the "Borrow Checker Practice" Lesson
>> Let's do this one more time. Let's create a function called print_all that takes an immutable borrow or a reference to items and prints out each item one at a time. Okay, we could do that. So I bet you can guess what that means. So print_all items, what is it?
[00:00:19] Well, it's a reference to a vector of item. And we're gonna print them out one at a time. So we can do this pretty easily. Let's go like this, for item in items, println, and then do that nice little, someone tell me the name of this operator. There we go, look at that.
[00:00:43] Okay, we wrote the function. Does that make a lot of sense here? Yeah, feeling good, isn't it? Yeah, okay, I'm gonna give everyone a second. So let's go back to our instructions now. Next, we're gonna create a vector of items called items. And we're gonna grab a mutable reference to item 0.
[00:01:02] So we're gonna call the function get_mut. So let me show you how that looks. So instead of having this right here, I'm gonna go like this. Let's take this and go vec bang and just have a single item in there. Does that make sense? There we go. I've just changed our one item into a vector of items.
[00:01:25] All right, now, let's grab a mutable reference to the first item. So I'll go like this, let mute, let's see, we'll go like this, let first =, I believe it has a convenience function, first_mut. There we go, look at that. Look at what we have. We have an option of mutable item.
[00:01:44] Of course, we have an option, right? That makes sense. There may not be a first item, but if there is, here's a mutable reference to it. Okay, fantastic, this is looking good. I've already deviated from the code that I wrote, I can already tell, but it feels good using this.
[00:01:58] All right, so I wanna print item 0. All right, let's do that. So I'm just gonna hop up here, grab that, hop back down here, paste that in, go here, go here, go here, and first. Feels good, gotta be as fast as possible when you're programming. So there we go.
[00:02:13] We're now gonna print out the first item. It'll be an option. So either we're gonna see none or we're gonna see some mutable reference to item. We actually know what we're gonna see, right? We kinda know what we're gonna see here, right? We're gonna see one item cuz there's only one item in there.
[00:02:31] All right, so now let's call print_all. What do you think is gonna happen when we call print_all? Anyone wanna exercise, Their ability?
>> Well, they're not write references, so we're not messed up there.
>> So what type of reference is this?
>> It's a mutable reference.
>> We can't have read references and one mutable reference.
>> Yes, we're having a read-write, right, right? Wrong, why is that wrong? Well, Rust is exemptionally smart, cuz we didn't follow our instruction list. How long does first live for? Rust is so good at identifying things that it knows that the flow stops here, and now it only goes here.
[00:03:30] So we actually only have a mutable reference out, and we never use it again, so its flow stops there. And then we have a immutable reference. So if we wanted to really see it explode, put one on the other side, and what do we see? Cannot borrow an item as immutable while you have a mutable reference out.
[00:03:49] It's saying, no way, cannot do that. And part of the reason for that is that we could change things about the underlying structure in which our pointer could be totally jacked up. And so you can imagine that you could have a reference to a vector that's mutable. And then you could have a reference inside that vector that's immutable, referring to the last element.
[00:04:08] Then our mutable one, we empty everything out. Then our reference would still be on something that doesn't even exist anymore, but we still have a reference to it. So this is part of the reason why there's this rule. You cannot have an intermixing of reference types, cannot exist because you will hurt yourself eventually.
[00:04:36] Who knows what's happening? You're just holding on to it, it's perfectly fine. So this is Rust trying to reduce what you can do. And so, of course, what rule did we break? Does anyone know? All right, let me find the rules. I've already stated it, but which one, what number?
>> We broke rule number 3?
>> Yes, we did break rule number 3, or technically, rule number 2, depending on how you look at it. All right. So here's the next one. The next one is gonna be kind of a fun one. It's pretty straightforward. Which is we're gonna get a mutable reference at item 0, we're gonna get a mutable reference at item 1.
[00:05:24] So here, we'll do that. We'll erase that and we'll go like this, get_mut(0). And so now we have these two. So we have two mutable references out. Is that what's happening right now? So if I go println, That and do first, What rule are we breaking right here?
>> More than one mutable reference?
>> More than one mutable reference. The easiest way to think about why that's not allowed is always just think of a collection. Just pretend you have an array or a vector. You can change it or another one could be referring to something within it and totally lose it and try to mutate something that doesn't exist.
[00:06:09] So you can always just only have one out. Look at that, so I've accidentally broke it, but now I'm gonna ask you a question. So I'm gonna do this. I'm not gonna hit Save. Does this break or is this valid?
>> It'll be valid, but there'll be a none.
>> Or even if there was, the thing is Rust can't tell if there's a none or not. Why is that valid?
>> First flow ends?
>> Remember the flow that I was talking about earlier? Rust is able to identify the lifetime of this truly is just this line.
[00:06:42] Thus, the next one right here mutates for the rest of the time. So it has these little immutable flows going on or mutable flows going on. So it can go, okay, you can mutate all you want right here, and that's fine. It's the moment you overlap them that it blows up.
[00:07:01] It's once they overlap, they explode. Until they overlap, you're perfectly fine. So kinda cool, right? That kinda makes it a pretty neat way to play around with this. And so that's just something to keep in your mind. The biggest thing, though, that you're gonna probably run into is always gonna be a value versus a reference.
[00:07:18] When you first start out, you try to pass things by value, right? You're gonna move it into a function. Just like when we first did that add_one right here, this one right here, we had it defined like this. That's just your biggest mistake and you run into this and it feels super confusing.
[00:07:34] Why is my stuff missing? I don't get it. It's because of that. It's because you're not used to using references. You're used to referencing being automatically done for you in a different language. So TypeScript, you pass in an object. It doesn't copy the object, it doesn't move the object, it just creates a reference for you and passes that in.
[00:07:50] So maybe Rust is just a little less magical, but that's what makes it so good. All right, so the question was not all free calls happen at the end of the scope, it happens where we stop using it. I don't know where the free calls happen. I would just never assume you know where they happen, unless if you know what Rust does underneath the hood.
[00:08:09] But Rust can validate that you don't break rules by looking at the flow of them, that's it. So I can't infer that that means a free call literally happens right after first was being done and used right here. I cannot say that there's a free call for first right here.
[00:08:24] It just has identified it's not breaking any rules here. So there's a difference there. All right, so when we first got started out, we had this little thing. You may or may not remember it, all right? This is gonna be so great. So when we first started out, we had this.
[00:08:44] What is happening here? Why is this breaking? Can we make it nice and beautiful for you? We have all this newfound borrow checker knowledge. What is breaking here? Let me give you a little bit of a hint, iter, it takes a reference to self. And it returns out a reference to the items that it's going over.
[00:09:17] Well, let's look at the errors. Okay, let's start way down here. Borrow later used here, okay? So that's probably not it. What's this one? Temporary value is freed at the end of this statement. What is the temporary value?
>> The reference to the vector?
>> No, we hold on to that one.
>> Value of the vector?
>> The vector, the vector is the temporary value. This only exists for the statement cuz no one's actually holding on to it. That is why I went like this earlier and did that, and now we can go data. Now, data lives for the entire thing.
[00:09:54] It didn't just live for that one little statement, it's living for the full function. So we get to define how long do you live? So hopefully that makes sense. It's kinda confusing, but in some sense, it makes a lot of sense, because no one holds on to it.
[00:10:10] Of course, it's gone. The only thing we're building is references to something that nobody holds on to.