DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Languages
  4. Ownership in the Rust Language

Ownership in the Rust Language

In this post, we take a look at the concept of ownership in Rust, and how ownership plays out in our Rust code. Read on Rustaceans!

Ayush Prashar user avatar by
Ayush Prashar
·
Nov. 08, 18 · Tutorial
Like (3)
Save
Tweet
Share
6.30K Views

Join the DZone community and get the full member experience.

Join For Free

One of the very distinctive features that makes Rust stand out from other languages is the way it does memory management.

Memory management in C happens by either declaring a variable or by allocating a chunk of space at runtime. That sotred data can then be utilized via explicit function calls. So, basically, the control resides with the developer which may result in error-prone code. In languages like Java, there is a concept known as the garbage collector. A garbage collector is a process which monitors the program for any memory leak and takes care of it. The memory is said to be eligible for garbage collection if there remains no reference to it or if it's in the Island of Isolation. A garbage collector is always running in the background and can not be explicitly controlled to free memory, which is where things may get a little out of hand.

Ownership, on the other hand, is different. It doesn't require you to explicitly deal with the memory or slow the program down while handling it. It works around the concept of scope.

A scope is the range of a line of code within which an item is valid. Generally, we mark the scope using curly braces ({ }). So a variable, for example, will be valid from the point of declaration until the end of its scope.

{
  // s is not valid here
  let s = "hello";  // s is valid from this point forward
  // do stuff with s
} // s is no longer valid

The variable to which the chunk of memory is allocated is known as the owner of the memory. The principles of ownership state that there can be only one owner at a particular time, and whenever the owner goes out of the scope, the respective memory is eligible to be returned to the OS.

There are two types of memory which are accessible to the code: stack and heap. Generally, when the data in use is of a fixed, known size, we push it in the stack. Since the stack always accesses data on the "top," this whole process becomes faster and efficient. But there are times when the fixed size is over the limit of the stack or perhaps we must allocate memory at runtime, this is where we use heap. The required memory on a heap is traced by the OS, and, if it's available, is marked as in use. After this, we push the pointer to that heap memory to the stack. Since this requires us to reach the memory using a pointer, the whole process is slower. So when we call a function, the parameter values, local variables, and the pointers to heap are all pushed onto the stack.

So whenever a variable leaves its scope, it gets popped off and things become pretty simple for data on the stack. The memory is returned to the OS once the owner goes out of scope. However, things are different when data is stored on the heap. Rust has a special method called drop which does the deed of returning the memory acquired by the owner in the heap to the OS;  drop is called whenever Rust encounters the closing " }."

Now, this may seem a simple change but it has a profound impact on how Rust deals with things.

Move: Interaction Between Data and Variables

In the code snippet given below:

let first_variable = 5;
let second_variable = first_variable;

we bind first_variable to a memory location which refers to the value 5, and then we create a copy of the value in first_variable and bind it to second_variable. Since integers are of a constant size and can easily be accommodated to the stack, this is as simple as it seems. However, when you consider complex data types such as a string, things are a little different. The string may be known during runtime, for example, a user input, hence its best to allocate it the memory on the heap. To identify this memory on the heap, we use three details, i.e. the pointer to the heap, the length, and the capacity. This information is pushed to the stack.

let s1 = String::from("hello"); //This is how we create a string
                                // in Rust
let s2 = s1;

So when we try something similar to the integer example, instead of copying the string itself, we copy the identifier information that we pushed to the stack. So now there are two variables referring to the same memory location. Now, this is where things get interesting. What if s1 goes out of scope? It should call the drop() method, right? Great. Now, what happens to s2? Which memory location would it refer to? What happens now when s2 goes out of scope? Which memory location would its call free?

This is where Rust plays it differently. As soon as a second variable points to the same location, Rust invalidates the previous variable. So in the case above, s1 becomes invalid as soon as s2 comes into the picture. Hence nothing happens when s1 goes out of scope.  drop() is called only when s2 goes out of scope. So instead of a shallow copy, this can be referred to as a move.

Passing Parameters in Functions

Another interesting outcome of this ownership situation is the way it behaves in the case of parametric function calls. Passing a variable as a parameter to a function is treated similarly to the move shown above. Basically, when a String variable is passed as a parameter to a function, its validity within the current scope terminates by default.

fn main() {
    let s = String::from("hello");   // s comes into scope

    takes_ownership(s);             // s' value moves into the
                                   // function and so is no longer
                                  // valid here

    let x = 5;                   // x comes into scope

    makes_copy(x);              // x would move into the function,
                               // but i32 is Copy, so it’s okay to
                              // still use x afterward

} // Here, x goes out of scope, then s. But because s's value was
  // moved, nothing special happens.

fn takes_ownership(some_string: String) { // some_string comes into
                                          //scope
    println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called.
  // The backing memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
    println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.

This was a brief introduction to the ownership paradigm of Rust. Feel free to provide your feedback on this blog or to initiate a discussion in the comment section.

References:
https://doc.rust-lang.org/book/2018-edition

Rust (programming language) Memory (storage engine)

Published at DZone with permission of Ayush Prashar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Kotlin Is More Fun Than Java And This Is a Big Deal
  • What Should You Know About Graph Database’s Scalability?
  • A Complete Guide to AngularJS Testing
  • How to Develop a Portrait Retouching Function

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: