Ownership: An exciting concept of Rust(Part-1)

Reading Time: 4 minutes

Ownership, An exciting concept of Rust and it enables Rust to make memory safety guarantees without needing a garbage collector. Therefore, it’s important to understand how ownership works in Rust.

While running all the programs manage the way they use computer’s memory. Some languages have garbage collection, in other languages, the programmer must explicitly allocate and free the memory. On the other hand Rust uses a third approach and that is: memory is managed through a system of ownership with a set of rules that the compiler checks at compile time. None of the ownership features slow down your program while it’s running.

Rules of Ownership

Keep these rules in mind as we work through the examples that illustrate them:

  • 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 of the variable

A scope is the range within a program for which an item is valid. Let’s say we have a variable that looks like this:


#![allow(unused)]
fn main() {
let string1 = "hello";
}

The variable ‘string1’ refers to a string literal, where the value of the string is hardcoded.

fn main() {
    {                            // string1 is not valid here, it’s not yet declared
        let string1 = "hello";   // string1 is valid from this point forward

                                 // do stuff with string1
    }                            // this scope is now over, and string1 is no longer valid
}

Basically, what is important is:

  • When  string1 comes into scope, it is valid.
  • It remains valid until it goes out of scope.

Let us take an example of string to understand this concept deeply.

We’ve already seen string literals, where a string value is hardcoded into our program. String literals are convenient, but they aren’t suitable for every situation in which we may want to use text. One reason is that they’re immutable. Another is that not every string value can be known when we write our code. For example, what if we want to take user input and store it? For these situations, Rust has a second string type, String. This type is allocated on the heap. And as such is able to store an amount of text that is unknown to us at compile time. You can create a String from a string literal using the from function, like so:


#![allow(unused)]
fn main() {
let string1 = String::from("hello");
}

The double colon (::) is an operator that allows us to namespace this particular from function under the String type rather than using some sort of name like string_from.

fn main() {
    let mut string1 = String::from("hello");

    string1.push_str(", world!"); // push_str() appends a literal to a String

    println!("{}", string1);      // This will print `hello, world!`
}

So, what’s the difference here? Why can String be mutated but literals cannot? The difference is how these two types deal with memory.

Memory and Allocation

In a string literal, we know the contents at compile time, so the text is hardcoded directly into the final executable. This is why string literals are fast and efficient. But these properties only come from the string literal’s immutability.

With the String type, in order to support a mutable, growable piece of text, we need to allocate an amount of memory on the heap, unknown at compile time, to hold the contents. This means:

  • The memory must be requested from the memory allocator at runtime.
  • We need a way of returning this memory to the allocator when we’re done with our String.

That first part is done by us: when we call String::from, its implementation requests the memory it needs. This is pretty much universal in programming languages.

However, the second part is different. In languages with a garbage collector (GC), the GC keeps track and cleans up memory that isn’t being used anymore. And we don’t need to think about it. If we don’t have garbage collector then it’s our responsibility to identify when memory is no longer being used. And call code to explicitly return it, just as we did to request it. Doing this correctly has historically been a difficult programming problem. If we forget, we’ll waste memory. When done it too early, we’ll have an invalid variable. If we do it twice, that’s a bug too. We need to pair exactly one allocate with exactly one free.

Rust takes a different path: Once the variable that owns memory goes out of scope, automatically the memory is returned

fn main() {
    {
        let string1 = String::from("hello"); // string1 is valid from this point forward

        // do stuff with string1
    }                                       // this scope is now over, and string1 is no
                                            // longer valid
}

Rust calls drop automatically at the closing curly bracket.

This was about Ownership: An exciting concept of Rust. Please stay connected for reading more such blogs.

Thanks for Reading!!

If you want to read more content like this?  Subscribe to Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe to Rust Times Newsletter: https://bit.ly/2Vdlld7.


Knoldus-blog-footer-image

Written by 

Ayushi is a Software Developer having more than 1.5 year of experience in RUST. Her practice area is Rust and Go. She loves to solve daily coding challenges.