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.

