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

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

Daily short news for you
  • Since the Lunar New Year holiday has started, I won't be posting anymore. See you all after the holiday! 😁

    » Read more
  • Continuing about jj. I'm wondering if there are any GUI software made for it yet to make it easier to use. There are already so many similar to git that I can't count them all.

    Luckily, the author has compiled them all together in Community-built tools around Jujutsu 🥳

    » Read more
  • Turso announces that they are rewriting SQLite in Rust. This adds another piece of evidence supporting the notion that Rust is "redefining" many things.

    But the deeper reason is more interesting. Why are they doing this? Everyone knows that SQLite is open source, and anyone can create a fork to modify it as they wish. Does the Turso team dislike or distrust C—the language used to build SQLite?

    Let me share a bit of a story. Turso is a provider of database server services based on SQLite; they have made some customizations to a fork of SQLite to serve their purposes, calling it libSQL. They are "generous" in allowing the community to contribute freely.

    Returning to the point that SQLite is open source but not open contribution. There is only a small group of people behind the maintenance of this source code, and they do not accept pull requests from others. This means that any changes or features are created solely by this group. It seems that SQLite is very popular, but the community cannot do what they want, which is to contribute to its development.

    We know that most open source applications usually come with a "tests" directory that contains very strict tests. This makes collaboration in development much easier. If you want to modify or add a new feature, you first need to ensure that the changes pass all the tests. Many reports suggest that SQLite does not publicly share this testing suite. This inadvertently makes it difficult for those who want to modify the source code, as they are uncertain whether their new implementation is compatible with the existing features.

    tursodatabase/limbo is the project rewriting SQLite in Rust mentioned at the beginning of this article. They claim that it is fully compatible with SQLite and completely open source. Limbo is currently in the final stages of development. Let’s wait and see what the results will be in the future. For a detailed article, visit Introducing Limbo: A complete rewrite of SQLite in Rust.

    » Read more

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? @@.

Theory vs. Practice

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!

Premium
Hello

Me & the desire to "play with words"

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

View all

Subscribe to receive new article notifications

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

Comments (1)

Leave a comment...
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
Scroll or click to go to the next page