1 Month Learning Rust - The Fundamentals of the Rust Programming Language

1 Month Learning Rust - The Fundamentals of the Rust Programming Language

The Challenge

It's a common story: the basics always seem easy to learn until we try to apply them in real-world situations, where numerous issues arise. Why is it that documentation provides only a few simple commands, but when we read a real project, it seems to be full of Martian syntax? @@.

banner

In reality, documentation should guide us from the fundamentals to advanced topics, rather than introducing complex syntax right away. If we have a solid grasp of fundamental knowledge, we can still write our first program. As we become more accustomed or work on various real-world projects, naturally, getting acquainted with new things becomes faster. Therefore, fundamental knowledge lays a strong foundation for us before we venture into more significant tasks.

Last week, I went through Rust's documentation from start to finish, focusing on syntax and the initial fundamental concepts.

Installation

Rust supports multiple platforms. You can install Rust with a single command if you're using macOS or Linux.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Or, you can explore other installation methods at Other Rust Installation Methods.

Hello World Program

Creating a "Hello, world!" program in Rust is straightforward.

fn main() {
    println!("Hello, world!");
}

println! is a macro, essentially a shorthand for a function. In this case, without using macros, you might have to write longer code. In Rust, you will encounter various macros to expedite code writing. println! functions similarly to console.log in JavaScript; it prints everything to the console screen.

Syntax for "building" the program into a binary:

$ rustc main.rs

You can then run the program using:

$ ./main
Hello, world!

To run a Rust program, you first need to compile it into machine code. Unlike JavaScript, where programs execute immediately without compilation, Rust also offers mechanisms for immediate execution, but I won't delve into that in this article.

Variables, Data Types, and Functions

In Rust, you declare a variable using the let or const keyword.

let a: i32 = 1;
# or let the type be inferred
let a = 1; // a: i32

As Rust is a statically typed language, variables need to be declared with a type when initialized, or Rust will automatically infer the type for the first declaration.

By default, Rust variables are immutable, meaning their values cannot be changed. You use the mut keyword to declare mutable variables.

let mut a: i32 = 1;
a = 2;

Functions in Rust must be value-stable. Rust doesn't allow declaring functions with values that change based on variables.

const A: i32 = 1;
# or
const A: i32 = 1 + 2;

# Computing from a variable will result in an error
const A: i32 = a + 1; // error

Basic data types in Rust include char, string, integer, float, boolean, and data structures like Tuple and Array. For more details, you can refer to Data Types.

Functions in Rust can return data or not. If they return data, you need to declare the return type.

# Function without a return value
fn another_function() {
    println!("Another function.");
}

# Function with a return value
fn five() -> i32 {
    return 5;
}
# Or use a concise format
fn five() -> i32 {
    5
}

# Function with parameters
fn five(n: i32) -> i32 {
    n
}

In Rust, ending a line with ; is mandatory. However, if you don't use return, the line without ; is treated as the return value.

if…else and Loops

In Rust, if...else acts as an expression, meaning it can return a value or not, similar to functions.

# if...else without returning a value
let number = 3;

if number < 5 {
    println!("condition was true");
} else {
    println!("condition was false");
}

# if...else returning a value that can be assigned to a variable
let condition = true;
let number = if condition { 5 } else { 6 };

When a value is returned, there must always be an else, and the return type must be consistent.

Unlike JavaScript, where evaluation expressions can be data or logical operations, Rust mandates that the expression in if must be a logical expression, meaning it must be either true or false.

The basic syntax for looping is to use loop.

let mut counter = 0;

let result = loop {
    counter += 1;

    if counter == 10 {
        break counter * 2;
    }
};

Additionally, Rust has while loops.

let mut number = 3;

while number != 0 {
    println!("{number}!");

    number -= 1;
}

And for loops.

let a = [10, 20, 30, 40, 50];

for element in a {
    println!("the value is: {element}");
}

for can be used with any data type that can be iterated, similar to JavaScript. For example:

for number in (1..4) {
    println!("{number}!");
}

(1..4) is a u32 array structure, so for iterates through each element and executes the statements inside the curly braces.

Structs

Struct is a data structure with attributes and associated methods, similar to a Class in object-oriented programming.

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

let user = User {
    active: true,
    username: String::from("hoaitx"),
    email: String::from("[email protected]"),
    sign_in_count: 1,
};

Struct also has a syntax similar to Spread syntax in JavaScript for overriding data.

let user2 = {
    username: String::from("2coffee"),
    email: String::from("[email protected]"),
    ..user
};

To declare methods for a struct, you use impl.

impl User {
    fn sayName(&self) -> String {
        self.username
    }

    fn sayEmail(&self) -> String {
        self.email
    }
}

let user = User {
    active: true,
    username: String::from("hoaitx"),
    email: String::from("[email protected]"),
    sign_in_count: 1,
};

println!("user say name {}", user.sayName);

self is similar to this in classes, used to refer to its attributes and methods.

When methods don't have self, they are called associated functions. They are used to create a predefined instance of the struct.

impl User {
    fn default(size: u32) -> User {
        Self {
            active: false,
            username: String::from(""),
            email: String::from(""),
            sign_in_count: 0,
        }
    }
}

let defaultUser = User::default();

This is reminiscent of the String::from() method.

A Brief Explanation of Memory in Rust

There are many new concepts here. For example, the String::from() syntax for creating a string in Rust. Why is it so complicated? In contrast, in JavaScript or many other languages, you simply put them in single ('') or double ("") quotes. To clarify, we need to familiarize ourselves with stack and heap memory in Rust.

Stack memory is used to store fixed-size variable values, such as char, str, int32, int64, and so on, whereas heap memory, on the contrary, has variable-sized data depending on runtime. Data within double quotes ("") is identified as char Data within double quotes ("") is str, while using String:from() makes it String and stores it in the heap.

So, in summary, what's the difference between str and String? It's primarily in memory management in Rust. Rust lacks automatic garbage collection, so it had to develop memory management for variables, or in other words, the variable's lifetime in the program.

Are all variables immutable by default in Rust? Yes, that means they cannot be changed after declaration. Don't worry; Rust still allows us to declare mutable variables, which is a kind of "102" memory management mechanism. We'll explore this further in the next article!

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.
Author

Hello, my name is Hoai - a developer who tells stories through writing ✍️ and creating products 🚀. With many years of programming experience, I have contributed to various products that bring value to users at my workplace as well as to myself. My hobbies include reading, writing, and researching... I created this blog with the mission of delivering quality articles to the readers of 2coffee.dev.Follow me through these channels LinkedIn, Facebook, Instagram, Telegram.

Did you find this article helpful?
NoYes

Comments (1)

Avatar
Ẩn danh1 year ago
''Dữ liệu trong dấu nháy kép ("") được xác định là char''. theo mình biết thì char là kiểu dữ liệu 4 bytes và trong rust nó hỗ trợ unicode. Còn char mà bạn nhắc đến trong bài viết được đặt trong "" thì nó là str
Reply
Avatar
Xuân Hoài Tống1 year ago
Cảm ơn bạn đã chỉ ra sai sót, mình đã cập nhật lại bài viết :D