Ownership

6 minutes
Share the link to this page
Copied
  Completed

The very core of Rust. What makes it special and interesting compared to other systems programming languages.

In this video:

  • The 3 Rules of Ownership
  • Stack and Heap refresher
  • How ownership works under the hood, with Strings as an example
  • Drop

Transcript

Let's talk about ownership. ownership is actually why I chose to learn rust in the first place. A few years ago, I decided I really wanted to master a systems programming language. I don't know about you, but I don't have a lot of extra time in my day. So if I was going to master a systems programming language, I wanted to make sure it would be worth it. I spent a month researching all the options.

And it really came down to three viable choices in the end, C or c++, both of which I had already had painful experiences with or an up and coming new language called rust rusts ownership model is the reason that I chose to spend my extra time focusing on rust. ownership is what makes those crazy safety guarantees possible and makes rust so different from other systems, programming languages. ownership is what makes all those informative If compiler error messages possible and necessary, there are three rules to ownership. First, each value has an owner, there is no value in memory, no data that doesn't have a variable that owns it. Second, there is only one owner of a value. No variables may share ownership.

Other variables may borrow the value, which we'll talk about in just a minute, but only one variable owns it. Third, when the owner goes out of scope, the value gets dropped immediately. Let's see ownership in action. Let's create a string s one and then create another variable s two and assign s ones value to it. What happens to this string is not a copy, at this point, the value for s one is moved to s two because only one variable can own the value. If we try to go ahead and use s one after this point, we get a compiler error.

Borrow have moved value as one. So what's going on here? Well, we've got stack and heap sections of memory. Just a super quick refresher, the stack stores values in order, which it can do because values are always fixed size. And the last value in is the first value out, which all tends to be very fast because it's so compact and predictable. The heap In contrast, stores values all over the place.

And values tend to be all sorts of sizes and don't get dropped in any order that corresponds to when they were created, which tends to make using the heap slower than this stack. Because you're always hopping around memory flushing and reloading your CPUs memory cache, what does that have to do with a value being moved? Let's walk through it. First, we create s one, a pointer, a length and a capacity get pushed onto the stack. The value of capacity in this case is three, the length is three and the pointer points to some newly allocated bytes on the heap. This all together is the value of the string s one, then we move s ones value to s two, because we can only have one owner, it works like this.

The pointer length and capacity all get copied from s one and pushed as new values on the stack as part of s two. If we stopped here, then memory safety would be non existent, which is why rust immediately invalidates s one, it still exists on the stack, the compiler just considers s one now uninitialized and won't let you use it after this point. It's more than a shallow copy. It's a move, which is why we can't use es one anymore, the value has moved to s two. Technically, if s one were immutable, we could assign it some new value and then use it again. But since it is immutable, in this example, it's just garbage and we can't use it anymore.

What if we didn't want to move the value but copy it instead? To make a copy of s one we would call the clone method. Why is it called clone instead of copy? Let me explain what it does under the hood and then I'll tell you clone performs the same initial copy of the stack but then also copies the heap data and adjusts s twos pointer to point to it. In both the move and the clone situations the three rules are satisfied one devalues have owners into only one owner and three when the variables go out of scope, the values will be immediately dropped. The stack and heap data if there is heap data together make a value.

Rust reserves the term copy for when only stack data is being copied. If there's heap data and pointer updates involved, then we use the term clone. In other languages you might call a clone a deep copy. When the value is dropped, that means the destructor if there is one is immediately run, the heat portion is immediately fried and the stack portion is immediately popped. So no leaks. No dangling pointers?

Yes, happy programmer. Another move situation, let's start with the same string and make a function that takes a string and returns nothing. If we pass s one to that function s, one is moved into the local variable s and do stuff, which means we can't use s one anymore because it got moved. So what do we do? One option is to move it back when we're done, we'll just make s one mutable, add a return type to do stuff, and then return s as the tail expression, which gets moved back out of the function and used to reinitialize s one, but that's usually not the pattern that you want. Passing ownership of a value to a function usually means a function is going to consume the past in value.

For most other cases, you should use references, which is why it's time to talk about references and borrowing in our next video

Sign Up

Share

Share with friends, get 20% off
Invite your friends to LearnDesk learning marketplace. For each purchase they make, you get 20% off (upto $10) on your next purchase.