About this video
What You'll Learn
- Explain how Rust's borrow checker enforces ownership, mutable borrows, and immutable references.
- Walk through Rustlings exercises that move vectors through mutable references and dropped borrows.
- Distinguish owned `String`s from `&str` slices and use explicit lifetimes to return references safely.
Ana Hobden joins David to work through Rust's borrow checker and lifetime system using the Rustlings move-semantics exercises, covering ownership, mutable references, scope, and when to reach for Box, Rc, and Arc.
Jump to a chapter
- 0:00 <Untitled Chapter 1>
- 1:00 Introductions
- 1:06 Introduction and Housekeeping
- 2:29 Introducing the Guest and Rust's Borrow Checker Overview
- 6:30 Mutability with Move Semantics 1
- 6:35 Starting with Rustlings Exercises
- 7:20 Rustlings Exercise 1: Mutability
- 11:10 Exercise 1 Solution & Discussion
- 15:40 Core Rust Ownership Rules
- 19:00 Mutable References with Move Semantics 2
- 19:02 Rustlings Exercise 2: Borrowing and Moves
- 22:00 Exercise 2: Passing by Mutable Reference Strategy
- 27:05 Rustlings Exercise 3: Borrow Lifetimes and Scope
- 28:30 Mutable References with Move Semantics 3
- 33:07 Exercise 3: Borrowing and Original Ownership
- 34:19 Exercise 3: Using `drop` to End Borrows
- 35:44 Rustlings Exercise 4: Creating and Returning Owned Data
- 35:45 Ownership with Move Semantics 4
- 38:00 Move Semantics 5
- 38:25 Rustlings Exercise 5: Multiple Mutable Borrows and Reordering
- 43:00 Lifetimes
- 43:05 Rustlings Exercises Summary
- 43:12 Introducing Lifetimes Concept
- 45:49 String (Owned) vs &str (Reference) & Explicit Lifetimes
- 51:01 Common Lifetime Error: Value Doesn't Live Long Enough
- 54:35 Related Concepts: Box, ARC, RC
- 55:24 Conclusion and Future Topics
Full transcript
Generated from the English captions. Timestamps jump the player to that moment.
Read the full transcript
1:06 Introduction and Housekeeping
1:06 Hello. Welcome to today's episode on Rawkode Academy. I wonder how many times I'm gonna mess that up over the next week, but welcome. Today, we are continuing our journey with Rust. We're gonna dive into one of Rust's more unique features, but also one of its most understood, especially by me, and problem domains when it comes to learning Rust. So before we get started, a little bit of housekeeping. First and foremost, if you wanna chat, drop as much comments as you want into YouTube's comments. We will do our best to get them on the screen. If you have
1:43 any questions, we'll do our best to answer them. In general, just let us know how you're getting on and how we can help. If you're not watching live, the best place to ask questions or join a conversation is on the Discord. So that is available at Rawkode.chat. You can come in there, join anytime, and ask any questions. Also, since the rebrand to the Rawkode Academy, we now have membership options available. You can join for a very tiny, tiny, tiny, tiny fee, and it just gets my eternal gratitude and support. So thank you to the members that have done that already.
2:14 Appreciate your support. And, of course, if you don't wanna do that, that's fine, but please subscribe and please click the thumbs up button on the video. This will help other people find this content. And today's episode is really important because this stuff is hard. And because it's hard, I can't do this alone. I am joined by my friend Anna. Hey, Anna. How are you? Hi. I'm great. How are you? I'm very well. All the better for us now being here covering this wonderful topic today. Would you please do the honor of just telling us who you are and please share
2:29 Introducing the Guest and Rust's Borrow Checker Overview
2:47 anything that you wish to with us and the audience? Gosh. I'm about a seven year Rust user by now. Seven? So I've spent a lot of years with the the Borrow Checker and Lifetime. Still get stumped sometime. Thankfully, the error messages are good. I live over in Victoria on the territory in Canada. This is my dog over here. For my work, I basically create a toolkit that lets you write Postgres extensions in Rust. Awesome. Yeah. So you spent a lifetime learning lifetimes. It it took me so long to hold that in, but I'm I'm glad I got
3:29 it out. I like it. So before I guess, I mean, I can always just throw the the the terminal up already, but why don't we chat for just a little bit? Like, Rust, I I don't think any other language has this concept of a bottle checker and lifetime. Is that right, or am I being a little naive there? I think if you look at tooling that exists in the C plus plus and C space around static analysis, a lot of them do very similar things to what the Rust Borrowchecker and Lifetime system give you. Fundamentally, they they don't come with, like, clang,
4:07 though. Okay. And you're gonna make this easy for me. Right? I'm gonna try. I'm not gonna I'm not gonna, like, stump you. I mean, I don't often get worried except when I actually have to show my own coding ability, in which case I do get a little bit of the fear. But I I I mean, I've gotta say, I've been writing Rust now for, I don't know, I I think nearly two years and I I just seem to like avoid these these things as much as possible and stick to writing really simple Rust code whenever
4:41 possible and that's worked really well for me. I think in fact, I'd probably bug you a lot now when I come across stuff and I'm like, don't understand it. But I mean, this stuff is not exactly the I don't know. It's not hard, but it's not easy either. Like I think if you just I'm hoping that if we just spend a little bit of time and cover the parameters, that this is just gonna click for me. That's what I'm hoping. That's what I'm trying to say. I'm hoping so too. Know, most of the Rust code I write is
5:11 extremely simple and, like, I call clone everywhere. And it you know, I feel shame sometimes, but, honestly, it's not a big deal. Like, the the cool thing about the Borrowchecker and Lifetime Systems is they allow you to do a lot of the deep subtle refinements that allow you to process terabytes of code very quickly. I've gotta be honest. I do I reach for dot clone an awful lot in my Rust code because I know it's just gonna remove the error. And again, I'm I'm avoiding having to learn that next thing that I need to learn. Either that or if I
5:45 that's a lie. What I do first is I add an ampersand, and if that doesn't work, I do a clone. And that is my entire knowledge of, like, the borrow checker, really. I'm just like, oh, is that gonna work? No. It's not gonna work. And then it said they'll say, oh, it doesn't implement copy. I'm like, why not? And then I just try and no. It's the oh, yeah. The clone doesn't work when it doesn't implement copy. And the ampersand doesn't work when it doesn't implement something else. Anyway, I am ruining my description of this. We're gonna rely on
6:12 your knowledge for today. So When I'm writing my Rust code, first I make it work, which usually means throwing clone everywhere. And then I make it right. And then I worry about making it fast. Well, I never have to ship code to production because I'm a developer advocate, so I don't care about the fast. But I would definitely like to learn today how to make my code at least a little bit more right. Yeah. Okay. Let's get my screen up. So we're gonna start, and I think we'll probably drift away from these relatively quickly and start
6:35 Starting with Rustlings Exercises
6:43 typing our own terrible Rust code. But the Rustlings example are a really good way for anyone watching that wants to learn a little bit about Rust. It has a whole bunch of exercises that introduce you to some of the common themes, enums, functions, scenarios, modules, macros, etcetera. We're gonna spend, I don't know how long, but a little bit of time on move semantics, and then we'll dive into the other components of Rust Odd ownership. At least unless I've never looked at Rust Rust You've never looked at Rust Odd. It's because you've already got all the knowledge. Like
7:18 so yeah. Let's take a look at the first example. Like I said, I think we will veer off and and type our own dot main dot r s or or whatever and just maybe run some code that way, but we'll start with this. We'll see what I actually don't know what it's gonna try and teach us. I'm hoping that's the right stuff and we'll just take it from there. Now, if I remember correctly, a smarter person would have checked before going live is that we can remove the I am not done comment and then run something on the command line and
7:20 Rustlings Exercise 1: Mutability
7:50 it'll tell us if it's correct. Seems to be the vape of Rustling. And we've got our first bit of code here. So what is this doing? So here we're here we're constructing a new vector. Right? So one of the first things you probably encounter in Rust is you wanna make an array of stuff or a list of stuff, which usually, you know, in Python, you do with your square brackets. In Rust, we have those. They're called slices. They are unowned, which means you don't actually, like, get to play with the array. This is just a usually, they're pointers to
8:33 slices. So vectors are actually owned arrays or lists or whatever you want to call them. So in here, we're actually creating a heap allocated owned pointer to this this memory buffer of at the what are we filling it with? We're filling it with I 30 twos. Yes. Okay. Let's Oh, I don't have Rust links. Please stand by. I mean, the Rust compeller doesn't take very long, so this will be really fast. Really? I think you're the first person I've ever heard not complain about the compile time of Rust. I was complaining about the compile time of Rust. I think I was just
9:18 too subtle with my sarcastic under twins. Oh, where's oh, am I gonna have to look at the the read me? I thought it was just cargo install. I can't believe I removed it from last time. There we go. That's what the curl bash. Nothing ever happened badly from curl bash. Don't judge me too much, Anna, please. It's fine. A curl bash wouldn't work on my machine at all, so I'm kind of jealous. Well, yeah, you've got to have derivations and safety and protections and all of this other stuff. Right? Yeah. I don't have a bin bash.
10:03 Does an external chip of, like, fake ones that link to something else, do I recall correctly, to try and make it sometimes work? Well, idiomatically, your bash script should be calling user bin n. Yeah. User bin n. Yeah. So that's what NextOS supports. That makes sense. Yeah. Alright. Well, it's actually gone a lot quicker than I expected, which is which is good. It's only got a few more packages left to build. And then I'm assuming and I should again know the run Rustling's move semantics one and it should tell us that it's failing. I don't think we need
10:41 the hint. We'll tell it to try and run it. And normally what it does, it'll give us an error message and then that'll tell us what it actually wants us to do. Looking at it, I'm assuming the challenge here is gonna be this. This println vec will be taking ownership or something and then the push is gonna fail. I think that's what it's gonna yell at me for. Do you think I'm right or am I wrong already? I don't know. Let's I'm looking at it and I'm like, okay, we've got the failed vac, return the vector.
11:10 Exercise 1 Solution & Discussion
11:24 So so what's the exercise here? Because I feel like this might compile. Well, let's just run it and see. So we don't need to we can run Rustling. Move semantic one. Oh, what's the thing? It was in the top of the code file. It was in top hint, and I don't think we need the hint yet. Oh, yeah. Run my exercise. Okay. So press things. Run. Oh, you type like me. What you mean? You also mistype all the time. Oh, yeah. That that's just the rule of being on a a livestream. Yeah. Rustler, I'm not running a lot. Yeah.
12:14 Because this is the fresh colon of the Rustlings, and I'm not gonna walk through all of them to get to this stage. We'll just use one. But we have our error message. Yeah. So you might notice if you haven't looked at Rust before, which you have, so that the error messages are quite verbose and good. It does explain what's happening. So we have a we've got this VEC one that we've declared with a let binding, and it's not mutable. And that's because mutability is opt in in Rust, which is a very nice thing. Okay. So our first error message is just telling
12:55 us that we need to make this mutable. Done. Is that it? Let's see. Well, that was easy. So you were almost right that it would compel if we just missed immutability. And I'm over here trying to overthink the ownership of this vector going into the print function or the print max. So why do you think we didn't have an ownership problem here? Just because this fill vac one takes a vector and also returns it. Right? Which line? Sorry? So if you look at line 18, the fillvec function Mhmm. It takes in an immutable vector, and it
13:40 returns a vector. Right? But you see at line 19, we actually can opt in to mutability there. Oh. Oh, yeah. Because okay. So we're getting a vector back here that isn't mutable, but we can actually tell it to become mutable. So we're like, what what's happening here? Yeah. So two different things are happening. Right? So to me to push onto a vector, you you need to mutate it. So when we made vec zero on line seven, and we passed that into the function on line nine, so we created it as immutable, and it arrived into the fillvec function
14:25 as an immutable vector. And on line 19, we opted into mutability on it, and then we returned it back on line 25. So you can see for line 21 through 23, we had mutability. Oh. Right? But before you added that mute on line nine, that mutability did not persist past the function return. Right? Yes. That's I think that's the lesson here. So I'm curious about this line here. Right? Where we're doing move back equals back. Like, it's still just one slice in memory. Right? There's no copy. There's no cloning. This is just that we take a mutable reference to
15:12 it. Yeah. This is all static. Like, the mute goes away at compile time. Can I do that more than once? Yeah. You can. Does that mean there's two mutable references to the same slice? This one might not work. Yeah. Okay. See, that that one won't work. But the first with just one, that's just basically it's called variable shadowing. Okay. So because nothing has mutable ownership of the vector, we can just take that here, but then we lose the ability to do that again here. Now Yeah. We return. That teaches us one of the fundamental ownership
15:40 Core Rust Ownership Rules
16:00 rules of Rust is that there can only be one owner of anything. There are rules? Yes. Okay. So there can only ever be one owner. There's three fundamental rules to ownership in Rust that everything only has one owner. There that owner can give out either one mutable reference to the thing or any number of immutable references to the thing at any given time. Okay. Got it. That makes sense. Yeah. So I think I understand. Okay. So I understand this. So this is shadowing the return drops immutability, but then we can pick it back up here.
16:47 Right. Okay. And is that just something we're supposed to know then? Like, when we have a function and we take mutable ownership of something and then we return So if if you think about this modify this? Could I could I tell it it returns a mutable reference? No. Yeah. No. That won't work because one of the thing like, the fundamental ideas of Rust that I really appreciate is the code you're looking at is very localized. So if I'm looking at a function, the only thing that I'm looking at in that function is what's gonna affect the function.
17:20 So the signature tells me that it takes in a vector and it returns a vector. But the return type, it doesn't have, like, mutability or anything because that would change how main was working. And that would make it very confusing to understand for the programmer. Right? Right. Got it. If you do want structures with mutability inside of them without requiring your users to explicitly say mute, you can use techniques such as a cell for that. Yeah. Again, that's one of those other constructs of Rust I have avoided as much as possible. So, I mean, if we need to delve
18:05 into that today, then we definitely can. But I I don't we don't need to for this example right now. Right? No. Okay. That really, really helps. I've gotta say it like that. I had I had seen lines like this before. I had just kind of ignored them. I hadn't really understand how they worked, but now I understand that, you know, we can have that one owner. It takes it and then releases it. So If you took this code and you moved it into the main function instead of the fillVec thing, and you did let mute VAC equal VAC
18:39 and then did your pushes, and then you went let VAC equal VAC again, you could just temporarily opt in to mutability in your main function. This can be really handy when you're doing fairly complicated things where you only want temporary mutability so that later in the function, you're not worried about it. Okay. Awesome. Let's move on to move semantics two. Let's get the error message this time and give ourselves a hint. Let's see what we got here. Oh, we actually have a move now. Oh, there's that thing that I see all the time. That's the one.
19:02 Rustlings Exercise 2: Borrowing and Moves
19:24 Oh, my face is covering it. It says does not implement the copy tree. So yeah, that's what I'm very familiar with. So that is on line line 10 is the move and we're trying to use it on 13. So this code looks, oh, yeah. This function is just pretty much the same. Right? So Yeah. It's almost the same as before, but you can see the print that we're doing is trying to use the vector that we created and then passed into fill vac. Are there you mean the here? Yeah. So we create vec zero on line
20:05 eight, and then we we consume vec zero Ah, right. In line 10, and we give it to the filvec function, what filvec returns is a possibly a completely different vector. So Okay. The fact that doesn't exist anymore. Right? Right. Well, after line 10, we have no guarantee that, yeah, it it's consumed. It's moved. So I think one way we could fix this is make fill back return or accept a pointer, like a reference. So this is where you do your ampersand. Right? So we mark the type as a reference here. Yeah. Do we have to also tell it
20:50 to pass in a reference? Right. And now this won't work. Right? Because we're we've got an immutable reference, and we're gonna be trying to make it mutable. So this is gonna give us problems. So if we try and compile this, you'll see a fun error message. Alright. So we get expected a vac, and we found reference to a vac. Oh, this is not the error message I was expecting, but it's also one. So fundamentally here, like, if we go back to the code, if we're only taking in a reference to a vector, we can't return an owned vector.
21:29 So just make it a reference too? Right. But this is still immutable this whole time. So this is just it's really not great. Okay. See, this is giving us error that you're behind a a normal immutable reference. Okay. So I'm assuming that the approach of passing on a reference to is potentially the wrong path. I mean, should we Well, I guess what we have to consider is what's the intent here? Is the intent to create a new vector and return it, or is the intent to fill up the vector provided? Because I think if if we made fillvec
22:00 Exercise 2: Passing by Mutable Reference Strategy
22:14 take a mutable reference to this this collection, then it it wouldn't need to return a completely new vector. Right? It could just mutate the argument. Okay. So we want it to be mutable. Yeah. So we did this, which means we have to update the signature here. Correct. And then we could get rid of the return type. Get rid of it? Would you Sure. Why would that? This doesn't need this. We don't need to shadow anymore, right, if we have a We won't need to do that either. Yeah. This is great. Okay. Now would we return
22:53 this or would we return the mutable reference again? Well, I don't think we need to return anything. Right? If we're not passing in the vector, we don't need to get it back. Oh. So that? Yeah. I think that would work. And then do have we we forgot about the Oh, the same one. Yeah. Line 10. Yeah. So we need to change the VEC one calls to VEC zero. Yeah. But I think that'll work for us. Oh, jeez. Alright. So we cannot back viral as mutable on line 15. So what happens here? Well, we've created vec zero as an immutable,
23:47 and now we're actually trying to mutate it twice, which is just it's a no no. So we're gonna have to make the vec zero immutable on line eight. Right? And then we'll have a mutable vec zero to pass a mutable reference into fillvec. I mean, could I do I don't think this will work, but try it. See, still wants a mutable one. Alright. Okay. So we make it mutable from the start. Now if so let's just I don't know. We've got a mutable vector here. Right? So Right. I'm curious. Do we still need would that not be passing a mutable reference
24:36 though or does that make it Why don't we why don't we try it? Come on. Like, the compiler messages are great. We could find out. Okay. Let's I think you will need it there, though. Alright. Okay. Because you need a mutable thing. Yeah. Okay. See, it doesn't like us. So even though it is mutable, when you're working with function signatures, you always have to be pretty explicit then. Yeah. Okay. I'm gonna break that down. So we had two we had vec zero and vec one, but what we've found is because we're doing a mutation and a function and then a mutation
25:17 with better function, it's actually probably just best to have a mutable reference anyway, and then Well, we we discovered that we can't use a value that we've moved out. So you can't use after move. So we had to come up with some way to keep the original value around or be able to use it later. And so we just went for mutability. Okay. I don't know if this is a feeling that I have with the right Rust, but I I always feel like I try my best to avoid mutability whenever possible. Is that a goal, or is that just something I've
26:01 stuck in my head for no real rhyme or reason? This isn't bad code. Right? No. I mean, functional programming tells us you can do a lot of things without the ability. But fundamentally, they all end up at IO, right, which is always mutable. So I I don't think mutability is bad, but I don't think you should just blindly opt into mutability on everything. It should be something you only add when you need it. Remember reading a a book when I was younger. I can't remember what it was called, but it was like the anatomy of a computer program or something. And it
26:34 was like halfway through the book or three quarters through the book before they had an assignment, and they made a note of apologizing for having an assignment in the book. Jeez. It was a really good book about functional programming and how computer programs work, but the fact that they apologized for the assignment just kinda always resonated with me. Not resonated, but it stuck with me is what I mean. I don't know. I'm a big fan of curse code, so I'm I'm fine with that. That kind of thing. Alright. Well, again, we're on a second example
27:05 Rustlings Exercise 3: Borrow Lifetimes and Scope
27:06 and this definitely helps. Russell seemed to have a different solution. So you got a different feel me just type it in the chat, Russell. We'll move on to number three just now. I wonder, like, if we use the hints, will it tell us how they want to just solve it or does it oh, yeah. We get we get some nice text. So it's confirming that our vec zero was moved into the function fillvec when we called it, which means that it gets dropped. Okay. Dropped towards not disappeared. No longer exists. Dropped. Okay. That seems more
27:46 technical. We went for the third option here. Which is to make it fill make it mutable. Yeah. Okay. So the other options were make another separate version of the data. So So would that have been a a clone? Yeah. And then option two was we could have oh, okay. So just do the colon and the subsequent function. And then Yeah. Okay. So our our options for filling mutable or clone the data, which there are two different ways to solve the problem and each has a trade off depending on each individual use case. Rust does a
28:22 good job making you feel guilty for doing heap allocations. Yeah. I I don't care where I allocate things. I just want to make to add new lines to this one. Oh, wait. What? No lines for multiple semicolons. No new line. Okay. Got it. Alright. Let me see if I can identify the problem then with this because I'm over two so far. So we have a vector. We create the mutable reference like we did before based on the result of the fillvec. We then because this is the same code. No, no, no. Okay. So the printvec one push and
28:30 Mutable References with Move Semantics 3
29:06 then printvec one again. So the problem here, that looks okay to me. I'm gonna run it. I mean, I know it's not okay, but I'm gonna say like, I don't think that the push function moves anything in memory? That should be okay. Oh, no. Don't read it. Oh, it is. The print has taken the ownership over here. Yeah. So my trick is this. That's what I always do first. Thanks, Rusty. No. No. I'm glad. Was great. Yeah. We added new lines now. Not my fault. It was Rust format or Rust font depending on how you wanna say
30:01 it. So I mean, it didn't complain about me passing these in as a reference to Yeah. So I'm not sure why this is telling me So this is in Philvec though. Oh. Yeah. Right. Okay. So there's nothing to do with that print statement at all then. Okay. We're passing in. Okay. I got it now. It's not mutable when we pass it in here. We're not doing the shadowing and we cannot do the shadow because we're not able to add in lines. Gotcha. Right. Okay. So Got it. So we need to be able to make okay. That's tricky.
30:54 Damn you, Rustlings. I mean, man, even I think but we tried that with the last one it didn't really work. So I I think I think that's a worthwhile thing to pursue this time. Okay. So what this That will imply we have to make vec zero also mute though. Would this not just take ownership of it here? Well, no. No. So we in order to give out a mutable reference, we need to have a mutable ownership. Right. Okay. Yeah. So we've not added any new lanes. No. We've only changed them so far. And I guess this would have to be updated
31:44 to do this. Yeah. Does it say I can delete lanes? I mean, that's our solution to the last one, isn't it? Well, I don't think we should hold on. Let's play let's play the game properly. Right? Let's let's try and return the the mutable pointer to the vector here. Alright. Like so. Yeah. Let's do it. It worked. Wow. Alright. I'm confused. Well, because, you know, we talked about how you're allowed to have one mutable reference passed around. Right? Mhmm. So the in nine, we create the mutable vector. And in 11, we get the reference. Then down in 20,
32:32 we accept the argument also as a mutable reference, and then we return that same reference. So we're not creating any new reference. Is there any value to this approach over, like because, I mean, we need to move the return previously. Is that the same code or is it is it different? Basically the same. Okay. This is teaching a slightly different lesson that, you know, you can return a unique mutable reference and still work. You can still get a mutable reference. Right. Okay. Do you need vec zero after line 11? Alright. Question from Russell is, do we need vec zero after lane 11?
33:07 Exercise 3: Borrowing and Original Ownership
33:12 I do not think we do. Well, vec zero will accept will be dropped. Right? Because vec one now holds the reference. Well, no. Because it's it's a mutable reference. Vex zero will still be around. But doesn't vec one take ownership of it here? Like No. Can I I do a print l n Yeah? Vec zero? Yeah. That's gonna work? I think so. Oh, see. Way it's just a formatter. That message, I know all too well to Immutable borrow. That's fair. So what that's saying, if you look at that, is on on line 11, we took a mutable borrow.
34:06 And then we're trying to use that same thing we have a mutable borough out for before we get back that mutable borough. So if you look at the code again, because we have VEC one around, it's not gonna work. But, like, say you moved line 13 down to line 20, and before line on line 19, you put drop VEC one. Now that should work because we've explicitly dropped VEC one, and so we we have the only ownership over zero. Tada. I hate computers. Yeah. I just assumed that when we did this, that this was just dropped
34:19 Exercise 3: Using `drop` to End Borrows
34:58 anyway. Well, because you're taking a mutable reference to it, main still owns vec zero. It's just given out a mutable pointer. And so fun to like, the the big the big idea here is that if I'm main and I give out a mutable pointer to vex zero, and then, you know, I'm done and I'm finished, but I haven't got back that vex zero pointer. I can't call drop on it yet until I get back that pointer that that reference. Right? Okay. Okay. So it it lets the compiler statically track when it will be able to
35:37 drop a value. Okay. I'm getting it. Yeah. Four, I am gonna I am gonna get this one though. I feel like I know everything there is to know, which of course is never gonna happen. But for factor this code, so that instead of having vec zero and creating a vector and main, we create an infill vec. Okay. So what we have here is vec zero being created. It's read only. And then we get a mutable back. Wait. Why is this not that argument? Refracture this code so that instead of having vec zero, we it within fillvec and transfer the
35:45 Ownership with Move Semantics 4
36:34 Ah, okay. So basically, this goes away, and we wanna be able to get a mutable reference back from field deck. So does that mean I think we want ownership. If you just stop here, the the thing you're doing here is is not like, I know it's not our final code, but this would never work because on line 21, you create this memory buffer on the heap. And then on you you return it out of the function as a mutable reference. But fundamentally, it won't live long enough. Oh, I see that message all the time too. Noise That wouldn't work. Yeah. So
37:19 in order to do that, you'd have to return the own value. Yeah. So really what we want is vec equals Yeah. Like vec new or whatever. Well, I guess it would guess a tape, but I've I've taped it there. So Yeah. Oh, no. But it would be vague. Okay. So then we have this is not mutable, so I'm assuming we still need this. We push push, push and then this returns. So does this mean here because it's dropped from fillvec, the vec one is now the owner of this? Yeah. So at the end of the fillvec call, when
38:00 Move Semantics 5
38:00 you return the vector, the main becomes the new owner of that that variable. Yeah. Okay. So that should work. There we go. Tada. Okay. I get it. Kinda. Good. Alright. We've got one more. Number five. Took me a long time to search and internalize some of these rules, so don't feel bad. Oh, this one looks fun. Stars everywhere. Okay. So make me compile without adding, removing, or changing any lanes. What? Make me compile without changing any code. Is that what it's doing? Interesting. Okay. So we have okay. Let's let's go through this lane by lane because I I
38:25 Rustlings Exercise 5: Multiple Mutable Borrows and Reordering
39:01 don't understand this code. So on lane nine, we create a mutable variable called x, which equals an integer. Line 10, we have y, which is a mutable reference to that. And then on lane 11, we've got z which is a mutable reference to a d referenced y which would be x. So I mean, that's not legal. Right? Oh, let's try and and build this. Okay. Yeah. That's fair. So this is saying that we cannot use star y because it was mutable. Yeah. That's what I thought. Good. Because this is a mutable reference to x, and this is essentially doing the same thing.
39:57 Right? Star y is x. Is that right? Yeah. Okay. Yeah. So so how do we fix this if we can't change the code? I don't understand. Make me compile without adding, removing, or changing. That cannot be right. So do we have to add stuff like around main? But it says we can't change any should we have the I don't understand it. In main. Yeah. Maybe. Okay. Carefully reason about the range in which each mineral reference is in vogue. Does it help to update the value of Oh, oh, gosh. It says you can reorder them, which is definitely changing them.
40:49 But that's changing the lanes. I know. Okay. So now with this this new knowledge that we can reorder them, let's think about this. Okay. So we know that when we have y, we have a mutable the the unique mutable reference to x. Right? Correct. Yes. And when we have z, we also have a unique mutable reference to to x. Yeah. So I think if we reordered it so that we did not have z existing while we mutated y, it might work. Like that? I think so. That was easy. I knew it. Yeah. Yeah. So this only works. I don't know if I'm
41:48 yeah. Let let me try and articulate this. This only works because we don't use y anymore. Is that right? That's correct. Like, this one now cause it to fail again, I think. Yeah. Well, that and my inability to macro. Well, that still works. Really? Dog. Oh, but we're not mutating it. Right? So Yeah. That's fair. There we go. Okay. Well, that that that wasn't the right error. But, yeah, came with the no. That was you have to dereference the thing. But doing this would create a create a new integer. Oh, yeah. This is because these
42:43 these are fixed size integer. Okay. Yeah. Okay. These are these these integers implement copy. So Yeah. And Rust doesn't care about copy. Well, copy implied some things very cheap to make a copy of. So Yep. Alright. That really helped my understanding. Good. So we've got around fifteen minutes left. Will we will we tackle lifetimes? I don't I don't think we'll get that far, but I'd love to chat about them a bit. So I wanna go through that exercise that you you did with me when I was looking for help last week. Oh, no. Okay. I don't know if I can remember
43:12 Introducing Lifetimes Concept
43:29 it, but sure. Yeah. You have to remember all off the top of your head. Go. Okay. No. Alright. Let me create I'll just say Rawkode RS. So let's have a function, main print ln. This is about as good as my Rust gets. Sure. Cargo run. Really? It's gonna compare all of that for hello just because I'm in that directory. Interesting. Yeah. That's annoying. That's annoying. But I should've used Rusty. I don't know if that would've worked. I guess, only has to do it once. Oh, does that not work? Well, that doesn't run it. How can I
44:33 just run a fail? I think that that will have created a binary in your directory you can run. Yeah. To run the Rawkode. Alright. Okay. Can I not just tell it to interpret and run the file? You're not really supposed to, like, just ad hoc run Rust files. So I'm changing those rules today. Jeez. So, yeah, the exercise that you walked me through last time, which has been infinitely beneficial to me, and I think we should share it with people, is that I have ignored lifetimes for as long as I've been writing Rust because I just I really don't really want
45:16 to worry about it. Yeah. But the thing that you kind of may help me realize is that even if the lifetimes aren't in my code, they are still here. They still exist and we we can make them explicit. And and just that process really helped me understand it. So I'm not sure if we need to type new code or if we Yeah. We probably will, right, won't we? So should we have a new function that just prints out? Here, let's let's do like, let's make a string. Rawkode. It's in here. And we can print out that string. Right?
45:49 String (Owned) vs &str (Reference) & Explicit Lifetimes
46:07 And then, like, this is basically what we were doing before. Do do the thing. Yeah. And my awkward understanding of string and string and string and string, but just no much better now because of this. Yeah. So wrong. I don't really know what I'm doing here, but we're going to do it. Yeah, so this will give me the fundamental. We were talking about string and string and STR. Right? Yeah. That was right. Yeah. Yeah. So if you have a string, which would be this in here, you have ownership of a memory buffer of the, like, unsigned eight bit integers.
47:09 Now if I was to do, like, as STR here so that I got in some STR type, which I'm sure a lot of people are used to seeing in Rust. That is unknown. It's just a reference to one of those same memory buffers. So you can think of a reference to a string as an STR with a lifetime shorter than the string. So when we talk about lifetimes, if I made some string processing function, like split, took in one of these string STRs, which is one of these unknown slices So I didn't own the memory.
48:15 It would essentially be doing this where I have this lifetime, which is just a generic type. You know, if you're you've written a lot of Rust, you might be used to seeing generics like this. Lifetimes are just another type, basically. What's that saying is just that the x str has the same lifetime as the function's call. So you can think of starting here and then ending here. So when I pass in the STR up here, that that reference will only be alive until line 13 Okay. Because I'm not I'm not assigning it here. So if I had, like, let
49:12 ref thing equal this, and I did return that same STR, it would persist all the way through to here. Right. Okay. Yeah. So there's a big difference between an unowned type and an unowned type. References, as we saw in wrestlings, allow us to work with them. But when you get into scenarios where you references of references, lifetimes are really powerful because, you know, in this case, this is obvious. You know, if you give me a string and I return to you one of these sorry. If you give to me an STR and I return to you an STR,
50:00 they pretty much have to be of the same lifetime. Because if you give me unknown memory and I give you a pointer to unknown memory that's within that unknown memory, I can't do anything other than promise that it will be valid for as long as the unknown memory is valid. But let's say we were writing a split, so we we are actually splitting up this this this unknown slice. We might have some, you know, set of subsets of this STR. So we might return a back of STR instead. It still all have the same lifetime, so
50:44 it doesn't really matter. But I've probably written very broken code here, so I hesitate anyone trying to run it. But, you know, lifetimes Sorry. Let me go. Go ahead. I was gonna say, one of the weird situations I always seem to get myself into, and I've never really understood it, has been like I write a function, and it tells me that the value doesn't live long enough to be returned. So usually, that's because you're doing something like you're trying to return a reference to, say I did let a equal And then I return a. This would actually
51:01 Common Lifetime Error: Value Doesn't Live Long Enough
51:38 be quite valid, because the let a equals thing there is a static string gets actually written into the binary. So if I was to run string mean that is this? Yeah. That's that. Right. So the static means is that it's gonna be alive for the whole time. So, like, this is completely valid that you could make it like that. This is a valid function, and it's fine. Okay. But the moment I try and do let's say I do this and I do ref of a like that. And then I try and return ref of a.
52:34 Because I'm trying to return a reference to some memory that is only alive during the function call, this owned buffer, Mhmm. That value is not going to be around at the end after the end of the function call. So in main, when I called this up here, one thing like this. Yep. X in this case would be an invalid pointer. Right? Okay. So can we make this explicit? I wanna see if I understand this correctly. Yeah. We're saying all functions already have a lifetime. Where where does that go? Is it Yeah. So this is like an
53:14 a? Yeah. So like you could think of all functions having a lifetime. Then Which means that this here is lifetime of a. I don't even know what I'm like, that's reference. Would that have the same lifetime? You could think of this say say we're doing something really complicated with string processing, you might actually see something like this. No. I don't like it. Yeah. So this is about sub lifetimes. And what this is saying is essentially, like, all of the lifetimes b are shorter than or or, like, within the realm of a. And, yeah, you don't like that, but that's
54:06 fundamentally what's happening. It's like, this is some some random lifetime called who knows what. That's a sub lifetime of a. Right. Okay. Yeah. I think I I need to do some reading on life science. And I I based actually now just on where this going, I feel like this life science are probably an entire episode on their own with like, I don't even know sub life times were a thing. Sub life times. Yeah. Well, I you know, we could also talk about ARC and RC and how boxes work. There's lots of fun to be had.
54:35 Related Concepts: Box, ARC, RC
54:41 So boxes are just something that I've only just started using since I've been working on Hong Kong Triad just because I have so many different, like, subtypes on enums and I I kinda need to them to pass those around. So I feel dragged into learning that, but they're slowly starting to make sense. And I and I understand it. It's like we have this thing of size and a box is a way to pass around. Like, I think it's just a pointer to it. Is that right? Yeah. So turning something into a box puts it on the
55:10 heap. And then so if you do like a boxed in trait, that just basically is putting a pointer to the v table on the on the heap. Okay. I think what we'll do, just given the time and how much more stuff we could potentially go into, is I would just chat with you later and we'll maybe see if you're up for doing another episode where we look at all those other cool that I have no idea about. But that was really helpful for me. Like, just going through those most semantic things and having, you know, your experience help guide us and and
55:24 Conclusion and Future Topics
55:44 just even explaining it all really, really helps. And I hope that other people got value from that as well. So Yeah. I had a lot of fun. Next time, you'll have to let me make some some good examples too. No. That's not a lie on this show. No. No. All examples must be terrible, terrible, terrible. Although your examples were definitely perfectly fine. I brought the terrible. Thank you for I don't know. I wrote some pretty trash code there. Well, I think when it's in the name of explaining concepts, that that is perfectly valid. Perfectly valid code.
56:18 And we got a thank you from the Nina as well. I will reiterate that. Thank you so much for taking time out of your day and walking me through this. Like I said, that was really, really, really helpful. Looking forward me. And I'm looking forward to hopefully having another episode. And your dog is looking mighty cute in the background. Yeah. Well, feel free to connect with me on Twitter or whatever, and happy to say hi to all your folks. Awesome. Thanks so much. Alright. Have a a great day, and I will speak to you again soon. Thank you.
56:52 Bye.
Technologies featured
Meet the Cast
Stay ahead in cloud native
Tutorials, deep dives, and curated events. No fluff.
Comments