https://blog.logrocket.com/mastering-ownership-in-rust/ LogRocket Blog * Blog * PodRocket * Sign In * Start your self-service trial Share * * * * * Reply * 0 [original][original] Ukpai Ugochi Follow I am a female Nigerian with a Bachelor's degree in Marine engineering and Bootcamp certificates in Software development. I'm a full stack JavaScript developer (MEVN) stack. I love to share knowledge about my transition from marine engineering to software development in the form of writing, to encourage people who love software development and don't know where to begin. I also contribute to FOSS in my free time. Mastering ownership in Rust April 7, 2021 7 min read 2168 [Mastering-ownership-in-Rust] Rust has emerged for the fifth year in a row as the most loved programming language in a developer survey carried out by Stack Overflow. There are various reasons why developers love Rust, one of which is its memory safety guarantee. Rust guarantees memory safety with a feature called ownership. Ownership works differently from a garbage collector in other languages, because it simply consists of a set of rules that the compiler needs to check at runtime. In some languages that require a garbage collector, developers need to explicitly allocate working and free memory space. This can quickly become tedious and challenging when it involves large amounts of memory allocation. Thankfully, handling memory allocation is the purpose of the ownership feature in Rust. To understand how ownership works, let's begin with a deeper understanding of the stack and heap. What are the stack and the heap? The stack and heap are both memory storage functions that are available for your code to use at runtime. For most programming languages, developers are not typically concerned about how memory allocation goes on the stack and heap. However, because Rust is a system programming language, how values are stored (in the stack or heap) is essential to how the language behaves. Here's an example of how memory is stored in a stack: let's think of a stack of books on a table. These books are arranged in a way that the last book is placed on top of the stack and the first book is at the bottom. Ideally, we wouldn't want to slide the bottom book out from under the stack, it would be easier to pick a book on top of to read. This is exactly how memory is stored in the stack; it uses the last in, first out method. Here, it stores values in the order it gets them but removes them in the opposite order. It's also important to note that all data stored in the stack have a known size. Memory allocation in the heap is different from how memory is allocated in the stack. Think of going to buy a shirt for a friend. You don't know the exact size shirt your friend wears, but seeing him frequently, you think he might be a medium or large. While you aren't completely sure, you buy the large because he will still be able to physically fit into it, even if he's a medium. This is how memory allocation in the heap works. When you have a value (your friend) for which you don't know the exact amount of memory it will require (size of t-shirt), you request for a specific amount of space for the value. The allocator finds a spot in the heap that is big enough and marks that spot as in use. This is an important difference between the stack and the heap: we don't need to know the exact size of the value being stored in the heap. There is no organization in the heap as compared to the stack. It is easy to push data into and out of the stack, because everything is organized and follows a process. The system understands that when you push a value into the stack it stays on top, and when you need to take out a value from the stack, you are retrieving the last value that was stored. This is, however, not the case in the heap. Allocating on the heap involves searching for an empty space big enough to match the amount of memory you requested, and returning an address to the location which will be stored in the stack. Retrieving a value from the heap requires you to follow a pointer to the place where the value is stored in the heap. We made a custom demo for . No really. Click here to check it out. [] Click here to see the full demo with network requests Allocating on the heap looks like book indexing, where a pointer for a value stored in the heap is stored in the stack. However, the allocator also needs to search for an empty space that is big enough to contain the value. The ownership feature manages how memory allocation is done in the stack as well as the heap so you don't have to go through this complicated allocation process. Nevertheless, your program will behave in an unexpected manner if you don't understand the basics of memory allocation in the stack and heap and how ownership is able to take charge of these. Ownership rules Ownership has three basic rules that predict how memory is stored in the stack and in the heap: 1. Each Rust value has a variable called its "owner": let x = 5; // x is the owner of the value "5" 2. Each value can only have one owner at a time 3. When the owner goes out of the scope, the value will be dropped: fn main() { {// scope begins let s = String::from("hello"); // s comes into scope }// the value of s is dropped at this point, it is out of scope } How ownership works In our introduction, we established a fact that ownership isn't like the garbage collector system and, in fact, Rust doesn't deal with a garbage collector system. Most programming languages either use a garbage collector or require the developer to allocate and free up memory themselves. In ownership, we request memory for ourselves, and when the owner goes out of scope the value will be dropped and memory freed. This is exactly what the third ownership rule explains. To get better understanding of how this works, let's look at an example: String { // givesOwnership will move its // return value into the function // that calls it let someString = String::from("hello"); // someString comes into scope someString // someString is returned and // moves out to the calling // function } // takesAndGivesBack will take a String and return one fn takesAndGivesBack(aString: String) -> String { // aString comes into // scope aString // aString is returned and moves out to the calling function } The second ownership rule (each value can have only one owner at a time) makes writing functions overly verbose, as you need to return ownership of functions whenever you want to use them as seen in the example above. To return multiple values, Rust developers use tuple, but this tends to take a lot of time. The best method is to use the Rust references feature. References and borrowing With references, you can use a function that has a reference to an object as a parameter instead of taking ownership of the value. With ampersands (&) you can refer to a value without taking ownership of it. Our example function can now be written this way: fn main() { let s1 = &givesOwnership(); // moves its return value into s1 let s2 = String::from("hello"); // s2 comes into scope let s3 = takesAndGivesBack(s2); // s2 is moved into s3 println!("{}", s1); println!("{}", s3); // takesAndGivesBack, which moves its return value into s3 } // Here, s3 goes out of scope and is dropped. s2 goes out of scope but was // moved, so nothing happens. s1 goes out of scope and is dropped. fn givesOwnership() -> String { // givesOwnership will move its // return value into the function // that calls it let someString = String::from("hello"); // someString comes into scope someString // someString is returned and // moves out to the calling // function } // takesAndGivesBack will take a String and return one fn takesAndGivesBack(aString: String) -> String { // aString comes into // scope aString // aString is returned and moves out to the calling function } Another good example of how to use references is this example shown in the Rust documentation: fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() } Slice Instead of referencing a whole collection, you can reference elements that are next to each other in a sequence. To do this, you can use the slice type in Rust. However, this feature doesn't have ownership like referencing and borrowing. Let's look at the example below. In this example we will use the slice type to reference elements of a value that is in a contiguous sequence: fn main() { let s = String::from("Nigerian"); let a = &s[0..4]; // doesn't transfer ownership, but references the first four letters. let b = &s[4..8]; // doesn't transfer ownership, but references the last four letters. println!("{}", a); // prints Nige println!("{}", b); // prints rian } Conclusion Ownership is an important feature in Rust. The more a Rust developer understands ownership, the easier it becomes for him or her to write scalable code. The reason why many developers love Rust is because of this feature, and once you master it, you can write efficient code and predict the outcome without having Rust pull a fast one on you! In this article, we have seen the basics of ownership, its rules, and how to apply them in our programs. We have also looked at some of Rust's features that don't have ownership and how to use them flawlessly. To get more tips on Rust's ownership feature, check out their documentation. LogRocket: Full visibility into production Rust apps Debugging Rust applications can be difficult, especially when users experience issues that are difficult to reproduce. If you're interested in monitoring and tracking performance of your Rust apps, automatically surfacing errors, and tracking slow network requests and load time, try LogRocket. LogRocket Dashboard Free Trial Banner LogRocket Dashboard Free Trial Banner LogRocket is like a DVR for web apps, recording literally everything that happens on your Rust app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting metrics like client CPU load, client memory usage, and more. Modernize how you debug your Rust apps -- start monitoring for free. Share this: * Twitter * Reddit * LinkedIn * Facebook * [original][original] Ukpai Ugochi Follow I am a female Nigerian with a Bachelor's degree in Marine engineering and Bootcamp certificates in Software development. I'm a full stack JavaScript developer (MEVN) stack. I love to share knowledge about my transition from marine engineering to software development in the form of writing, to encourage people who love software development and don't know where to begin. I also contribute to FOSS in my free time. * Uncategorized * #rust << The case for using frameworks WebAssembly runtimes compared >> Adding Google Maps to a Flutter app [pink][pink] Pinkesh Darji Apr 9, 2021 6 min read Building cross-platform apps with Expo instead of React Native [5847][5847] Spencer Carli Apr 9, 2021 8 min read Top React toast libraries compared [nefe][nefe] Nefe James Apr 9, 2021 6 min read Leave a Reply Cancel reply Does something seem off? Email [email protected] [ ]