We now know how to assign variables, how to write comments and work with functions in Rust and now comes a concept that is unique to Rust Programming Language and that is Ownership. Ownership in Rust guarantees memory safety without the use of any Garbage Collector. We will look into what owendership is, what is borrowing and how Rust works with the memory.
Ownership
In other programming languages like C or C++ we have allocate memory ourselves to do some tasks and also you had to free the memroy yourself too. This not freeing memory can cause memory leaks and performance issues in the program. But Rust is different from these languages. Rust manages the memory with the help of the concept called Ownership while not slowing down the program.
Ownership is simple words is that every variable to which a value is assigned is the owner of that value and there can only be one owner at a time and when ever the owner goes out of scope, the value is droppped. To explain it in detail consider the following.
fn main() {
let name = String::from("Rust");
print_name(name);
println!("First: {}", name);
}
fn print_name(name: String) {
println!("Second: {}", name);
}
We are using the type String
here becuase the types we discussed earlier in the last post are allocate in the stack and are poped out when they are out of the scope but for this example we need a type that allocates the memory inside the heap. Heap is the place where the program dynamically allocates the memory.
If we compiled the code now using cargo run
(we will learn about cargo in the next section) we will get the following error.
Linux$ cargo run
Compiling rust v0.1.0 (C:\Rust\rust)
error[E0382]: borrow of moved value: `name`
--> src\main.rs:6:24
|
2 | let name = String::from("Rust");
| ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
3 |
4 | print_name(name);
| ---- value moved here
5 |
6 | println!("No. {}", name);
| ^^^^ value borrowed here after move
When we provided the name
var with type String
to print_name
function it transferred the ownership of the var name
to the function print_name
and the var got out of the scope and since there is only one owner to each value the compiler throws an error saying that the println!(name)
cannot access the var name
.
Borrowing A Value
To deal with the ownership problem (which is not a problem but a feature), we can use another concept called Borrowing. It does what it says. This means that we borrow a value from a variable and when the value is used it is returned back to the owner.
Now to change the code above we get the following.
fn main() {
let name = String::from("Rust");
print_name(&name);
println!("First: {}", name);
}
fn print_name(name: &String) {
println!("Second: {}", name);
}
The only thing that is changed now is that there is an ampersand in print_name(&name)
function call and in print_name(name: &String)
function creation. What this will do is borrow the value from the name
and when the function print_name
returns, it will also return the value to the owner name
and now if we compile the code it will look like this.
Linux$ cargo run
Compiling rust v0.1.0 (C:\Rust\rust)
Finished dev [unoptimized + debuginfo] target(s) in 0.68s
Running `target\debug\rust.exe`
Second: Rust
First: Rust
I hope i made the concepts of Ownership and Borrowing a little easier to understand but still I highly recommend this page on the official Rust website for you to read to better understand Ownership and Borrowing.