Hey People! This is my second post in the Rust Series. Checkout my last post about why Rust is so awesome and why it fascinates me. This post is about an important feature of Rust called ownership which makes Rust so unique and powerful. The ownership is pretty straight forward to explain but it has deep connotations for the rest of the language.
What is ownership?#
Ownership is how Rust handles memory allocation. Usually, programming languages uses a garbage collector to deallocate unused memory or the programmer should explicitly allocate or deallocate memory but Rust does neither of that. The memory is managed through a set of rules that’s checked at runtime.
Stack & Heap#
Before going further into ownership, we all have learnt about the stack and the heap which are used for memory allocation. You wouldn’t care much about the stack and heap in other common programming languages but in Rust, it plays a vital role. This will decide how your code behaves and is very important in the long run to ensure your code is memory-safe and efficient, while avoiding garbage collection.
Stack & Heap are the memory that will be used by your code at runtime. The data of fixed size will make use of the stack in which the size is known at compile time.
Consider the following, the variable stores the String Literal “Hello, world!” where the size is known at compile time so this will go into the stack.
let str = "Hello, world!";
So, what would happen for data of dynamic length? First of all what data will be of dynamic length? Take the user input as an example. You’ll never know the size of the data at compile time. This is where heap comes into the picture. The memory will be requested at the runtime and stored in the heap.
We’ve already discussed the overview of Ownership. It is a set of rules that’ll be checked during compile time. So, what are the rules?
- Each value in Rust has a variable that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
Scope & Ownership#
You’ve heard about scope and it’s a very common programming concept. Scope is where an item is valid and can be accessed when needed. What does it have to do with Rust? Well, we’ve discussed about ownership and Rust will drop the value from memory once the item goes out of scope.
We’ve a value of type String. A string is always different from a string literal: a string will be stored in heap and string literal is stored in the stack because the size of the string literal is known at compile time. From the above example, when we call
String::from("Hello, World!"); the String Literal is converted to of type String and thus the memory is requested at runtime. Here, the memory will be dropped after the scope with a special function called drop.
Move & Clone in Rust#
We assign a value to a and assign a to b. We then print both the values. A programming language with garbage collector will print both the value of a & b in both of the above cases. Ok, let’s compile the first one and what do we get?
Ok, we got what we expected. Now let’s compile the second one. We expect that the result will be “Hello, World! and Hello, World! are same” like what other languages with a GC will give us but no, that won’t happen. This happens in Rust because Rust only stores the pointers in a & b and the actual value is stored in heap. If both of the variables are valid, then, while dropping memory, Rust will try to drop the same value twice which will result in memory corruption. Thus, Rust invalidates the first variable and moves the value to b.
So, what if you want both a and b? That’s where clone comes into the picture. You can copy the value to the second variable thus two values are stored in the heap and will be invalidated separately.
If you’re thinking about what happens when a variable is passed to a function The value gets dropped from memory, obv. Or… what if you need the data after the function is called???!!!
Please wait…I’ll brb…