Rust Essentials

My recall of what are essentials in Rust. It is a learning documentation. It is "recall and spaced repeation" approach.

Why

In C#, a language I have been coding for years, and of course, love it, a common error is NullReferenceException. It means your code holds a reference that points to "nothing", there is nothing in that location in the memory. In other word, it is invalid.

Memory is a limited resource and many programs try to get a share. Memory magement is crucial to any programming language. Unused memory must be returned to the operating system. In C#, there is Garbage Collector (GC). It has some overhead on your program. I am far from knowning the detail here.

So far, as I know, Rust was designed with those problems in mind. The language makes it impossible for developers to make those mistakes.

Heap and Stack

They are two structures of memory to store data. Stack is designed to store fixed-size values such as numerics, literals. Heap is designed to store unknown size data, known as Reference type.

When your code declares a reference type, what the code holds is a pointer stored on the Stack. The pointer knows where to look for the value on the Heap. And NullRefrenceException is when the point is on the Stack, but the location on the Heap has gone.

Heap and Stack are essential concepts for developers to understand programming languages.

Mutate or Immutable by default

Another common, nasty problem is the "race condition" – many paths (code) modify to the same location (data). Imagine that you have some money in your pocket. Someone else "access" your pocket and get some of it without your knowing. Surprise! It is a host of unexpected errors especially in the production.

Rust is designed with "immutable" by default. This code is valid in many programming languages except Rust

let name: String = String::from("Thai Anh Duc");

name.push_str("Oh No!");

Rust requires your "awareness". If you intend to modify something, say it explicitly with the mut keyword.

let mut name: String = String::from("Thai Anh Duc");

name.push_str("You Rock!");

Ownership

This concept was new to me. I have not thought of ownership at the programming language level. The "Ownership" concept appears to me when I do the domain design, data model.

A value always has one and only one owner in a scope. A scope is denoted by . Once the scope ends, everything inside the scope is dropped unless ownership is moved.

Ok, so who is the owner? and what does it own? Let’s look at this code

let name: String = String::from("Thai Anh Duc");

There is a variable name which is a String that has a literal value "Thai Anh Duc". In short, there are "variable" and "value".
"Variable" is the owner.
"Value" is owned by the variable (the owner).

let name: String = String::from("Thai Anh Duc");
// This works fine because name is the owner
println!("My name is {name}");

// owner is moved to
let it_is_mine: String = name;

// This code will not compile because what name owns was moved to it_is_mine.
// In other word, name points to nothing
println!("My name is {name}");
// This is ok
println!("My name is {it_is_mine}");

The ownership is applied for reference types. Value types are different. The value is copied. Each variable owns its own data. The below code works fine

// This is a literal of string, it is fixed-size. The value is stored in the stack
let name = "Thai Anh Duc";
// This works fine because name is the owner
println!("My name is {name}");

// Another copy of data is created and copied_name owns the new data
let copied_name = name;

// Both work fine
println!("My name is {name}");
println!("My name is {copied_name}");

Move

The transfer of ownership is a Move. The actual data is unchanged (the data on the heap).

Copy

Another copy of data on the stack is created for the new variable. Both variables operate on their own data. It is safe.

Drop

When a scope ends, Rust performs a "Drop" to release memory allocated in the scope. Notice that a reference type is clean up unless its ownship is moved to the consumer. It is done by returning a value.

fn main(){
    let name: String = String::from("Thai Anh Duc");

    let hi = move_ownership(name);

    println!("{hi}");
}

fn move_ownership(name: String) -> String{
    let say_hi : String = String::from("Hi: ") + &name;

    say_hi
}

Reference

In the previous example, the &name is used to access the location of the variable. It is the pointer to a location on the heap

Bottom lines

I recalled and documented what I have learned from Rust Understanding Ownership.

Write a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.