Ownership in RUST - ELIF 5

Hello everyone ! A fellow rust learner here. It's been couple of weeks I've started learning rust. The most confusing part I found about rust as a programming language is the concept of ownership and borrowing.

We all are used to throwing a variable to a function using it later, or just re-assigning to multiple variables by making clones just because we can. But rust follows a strict practice here.

In order to make sure your code doesn't have any data race, it restricts a variable's value to be written only from one place. This also solves the problem of dangling pointers as rust has no garbage collector, it uses the variable scope to automatically clear the memory.

Ownership

In rust, every value has a variable which is called it's owner. And there can't be multiple owners at one time. When you initialize a new variable with a value it's said to be the owner of the value.

let name = "mahesh";

Here, name is the owner of the string literal "mahesh".

let name = "mahesh";

When you use this variable in another variable's assignment. The value is copied into another variable. Or, the variable is cloned. The two variable do not point to the same variable from memory's perspective.

This is because variables like integer, characters, booleans, raw strings implement the Copy trait. They require very less memory and can be copied easily to another. In other words, copying them is efficient enough for a computer.

But, this is not the case with structs or any other compound data types which are stored on heap.

#[derive(Debug)]
struct Age{
    num: i32
}
fn main() {
   let _name = Age { num: 30};
   let _another = _name;
   println!("{:?}", _name);
}

In this case, the variable _name is a struct. This _struct declaration doesn't implement the Copy trait. So, whenever you reassign the variable, it's owner is changed. At the line let _another = _name;, the ownership of newly created struct is shifter from _name to _another. Now, rust won't let you access the variable _name because a variable can only have one owner at a time.

But, this is not the case if you use #[derive(Copy)], it will create a brand new struct everytime you re-assign the variable if it implements the Copy trait.

error[E0382]: borrow of moved value: `_name`
 --> src/main.rs:8:21
  |
6 |    let _name = Age { num: 30};
  |        ----- move occurs because `_name` has type `Age`, which does not implement the `Copy` trait
7 |    let _another = _name;
  |                   ----- value moved here
8 |    println!("{:?}", _name);
  |                     ^^^^^ value borrowed here after move

The rust compiler is also pretty much self explanatory on this matter.

To avoid this from happening, you have multiple one options. One is to derive Copy trait as mentioned before. Another option is to clone the variable. You can either write your own clone method or derive the predefined trait and use it.

But these two are not the trivial solution always. So, the solution is to use referencing.

Referencing

Rust allows you to create a read-only or mutable reference to a variable. This allows you to safely access the variable but remember there can never be multiple mutable reference of a variable at once. Rust borrows the same & notation to create reference of a variable.

   let _name = Age { num: 30};
   let _another = &_name;
   let _other = &_name;
   println!("{:?}", _name);

The snippet shown above runs completely fine because in this case we are creating reference of the variable _name and we're good to do it. Because remember, this won't create multiple data and since there isn't a actual value stored at _another, there won't be issues like double-memory cleaning issue.

But, this isn't the case when you try to create multiple mutable references.

   let mut _name = Age { num: 30};
   let _another = &mut _name;
   let _other = &mut _name;
   println!("{:?} {:?}", _another, _other);

In this snippet you'll get a error as follows...

error[E0499]: cannot borrow `_name` as mutable more than once at a time
 --> src/main.rs:8:17
  |
7 |    let _another = &mut _name;
  |                   ---------- first mutable borrow occurs here
8 |    let _other = &mut _name;
  |                 ^^^^^^^^^^ second mutable borrow occurs here
9 |    println!("{:?} {:?}", _another, _other);
  |                          -------- first borrow later used here

This helps you to avoid data race at compile time.

I'm just getting started with rust migrating from high level language. Would love to hear any suggestions, feedbacks. Cheers!