Skip to content
DocsRust LearningbeginnerBasic Concepts
Chapter 2 of 19·beginner·7 min read

Basic Concepts

Khái Niệm Cơ Bản

Variables, data types, and functions

Hover or tap any paragraph to see Vietnamese translation

Variables and Mutability

By default, every variable in Rust is immutable. This is one of the design choices that makes Rust code safe and easy to reason about. When a variable is immutable, its value cannot be changed once it is assigned.

src/main.rs
1fn main() {2    let x = 5;3    println!("The value of x is: {x}");4    // x = 6; // ERROR: cannot assign twice to immutable variable `x`5}

To make a variable mutable, add the mut keyword before the variable name. This makes the intent explicit and helps readers of the code understand that the variable's value may change.

src/main.rs
1fn main() {2    let mut x = 5;3    println!("The value of x is: {x}");4    x = 6;5    println!("The value of x is: {x}");6}
Tip
Rust encourages using immutable variables by default and only using mut when truly necessary. This helps the compiler optimize code better and reduces logic errors.

Constants and Shadowing

Constants

Constants are declared with the const keyword and are always immutable — you cannot use mut with them. Constants must have an explicit type annotation and can only be assigned a constant expression, not the result of a function call or a runtime-computed value.

src/main.rs
1const MAX_POINTS: u32 = 100_000;2const PI: f64 = 3.14159265358979;3const HOURS_IN_DAY: u32 = 24;45fn main() {6    println!("Max points: {MAX_POINTS}");7    println!("Pi: {PI}");8    // Constants are valid for the entire program duration9    // and can be used in any scope, including the global scope10}

Shadowing

Shadowing allows you to declare a new variable with the same name, shadowing the previous one. The new variable can even have a different type. This differs from mut because you are actually creating a new variable — the old one remains immutable.

src/main.rs
1fn main() {2    let x = 5;3    let x = x + 1;      // shadows previous x (value: 6)4    let x = x * 2;      // shadows again (value: 12)5    println!("x = {x}"); // 1267    // Shadowing allows changing the type8    let spaces = "   ";          // type: &str9    let spaces = spaces.len();   // type: usize — different type, same name!10    println!("spaces = {spaces}"); // 31112    // With mut, you cannot change the type:13    // let mut s = "hello";14    // s = s.len(); // ERROR: mismatched types15}

Integer Types

Rust has many built-in integer types. Each can be signed (starting with i — stores negative numbers too) or unsigned (starting with u — positive only), in sizes 8, 16, 32, 64, 128 bits, or arch (architecture-dependent).

src/main.rs
1fn main() {2    let a: i8   = -128;        // 8-bit signed: -128 to 1273    let b: u8   = 255;         // 8-bit unsigned: 0 to 2554    let c: i32  = -2_000_000;  // 32-bit signed (most common default)5    let d: u64  = 18_446_744_073_709_551_615; // 64-bit max67    let e: isize = -1; // arch-dependent signed (for indexing/offsets)8    let f: usize = 42; // arch-dependent unsigned (for lengths/indices)910    // Integer literal formats11    let decimal     = 98_222;      // underscores for readability12    let hex         = 0xff;        // hexadecimal13    let octal       = 0o77;        // octal14    let binary      = 0b1111_0000; // binary15    let byte: u8    = b'A';        // byte literal (u8 only)1617    println!("{a} {b} {c} {d} {e} {f}");18    println!("{decimal} {hex} {octal} {binary} {byte}");19}
Tip
When unsure which integer type to use, i32 is generally a good default — it is fast even on 64-bit systems. Use usize when working with collection indices or memory sizes.

Floating-Point, Boolean, and Character Types

Beyond integers, Rust has three other important primitive types: floating-point numbers, booleans, and Unicode characters.

src/main.rs
1fn main() {2    // Floating-point (IEEE 754 standard)3    let x: f32 = 3.14;  // 32-bit float (single precision)4    let y: f64 = 3.14;  // 64-bit float (double precision — the default)56    // Arithmetic operations7    let sum        = 5 + 10;8    let difference = 95.5 - 4.3;9    let product    = 4 * 30;10    let quotient   = 56.7 / 32.2;11    let remainder  = 43 % 5;     // modulo (only for integers)12
Info
Rust's char type represents a Unicode scalar value, not a single byte as in C. Each char is 4 bytes and can hold any Unicode character, including emoji and CJK characters.

The Tuple Type

A tuple groups values with different types into one compound type. Tuples have a fixed length: once declared, you cannot add or remove elements. Tuples are very useful for returning multiple values from a function.

src/main.rs
1fn main() {2    // Explicit type annotation3    let tup: (i32, f64, u8) = (500, 6.4, 1);45    // Destructuring — unpack the tuple into separate variables6    let (x, y, z) = tup;7    println!("x={x}, y={y}, z={z}");89    // Access by index using dot notation (0-based)10    let five_hundred = tup.0;11    let six_point_four = tup.1;12    let one = tup.2;

The Array Type

Arrays group multiple values of the same type with a fixed length known at compile time. Arrays are allocated on the stack, not the heap. Use arrays when the number of elements will not change; use Vec when you need a growable array.

src/main.rs
1fn main() {2    // Type annotation: [type; length]3    let a: [i32; 5] = [1, 2, 3, 4, 5];45    // Initialize all elements to the same value6    let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]78    // Access elements by index (0-based)9    let first = a[0]; // 110    let last  = a[4]; // 511    println!("first={first}, last={last}");12    println!("length: {}", a.len());
Warning
If you try to access an element beyond the array bounds, Rust will panic at runtime instead of continuing with invalid memory like C/C++. Rust also checks bounds at compile time when possible.

Functions and Return Values

Functions are fundamental in Rust. The fn keyword declares a function. Rust uses snake_case for function and variable names by convention. The type of each parameter must be explicitly declared. The return type is specified with ->.

src/main.rs
1fn main() {2    greet("Alice");3    let result = add(5, 3);4    println!("5 + 3 = {result}"); // 85    println!("10 squared = {}", square(10));6}78fn greet(name: &str) {9    println!("Hello, {name}!");10}1112// Return type declared with ->
Info
In Rust, functions can be defined anywhere in the file — you do not need to forward-declare them as in C. The compiler finds them throughout the entire module.

Statements, Expressions, and Comments

One of the most important things to understand about Rust is the distinction between statements and expressions. Statements perform actions and do not return a value. Expressions evaluate to and return a value.

src/main.rs
1fn main() {2    // STATEMENT — ends with semicolon, returns nothing (unit type ())3    let y = 6;45    // A block is an EXPRESSION — the last line without ; is the value6    let z = {7        let x = 3;8        x + 1  // no semicolon → this is the value of the block9    };10    println!("z = {z}"); // 41112    // if is an EXPRESSION in Rust — it can return a value
Tip
Adding a semicolon to the end of an expression turns it into a statement, causing it to return () instead of its value. This is why a function's return value is typically the last expression without a semicolon.

Key Takeaways

Điểm Chính

  • Variables are immutable by default; use mut to make them mutableBiến mặc định là bất biến; dùng mut để có thể thay đổi
  • Rust is statically typed but can infer types in most casesRust có kiểu tĩnh nhưng có thể suy luận kiểu trong hầu hết trường hợp
  • Functions declare parameter types and optional return typesHàm khai báo kiểu tham số và kiểu trả về tùy chọn
  • Shadowing lets you reuse a variable name with a new typeShadowing cho phép bạn tái sử dụng tên biến với kiểu mới

Practice

Test your understanding of this chapter

Quiz

Which keyword makes a variable mutable in Rust?

Từ khóa nào làm cho biến có thể thay đổi được trong Rust?

True or False

Constants in Rust can be declared with the mut keyword to allow runtime changes.

Hằng số trong Rust có thể được khai báo với từ khóa mut để cho phép thay đổi tại runtime.

Code Challenge

Declare a mutable integer variable with value 10

Khai báo một biến số nguyên có thể thay đổi với giá trị 10

let  count: i32 = 10;
Quiz

What is the default integer type when Rust infers the type?

Kiểu số nguyên mặc định khi Rust tự suy kiểu là gì?

True or False

Shadowing in Rust allows you to reuse a variable name with a different type.

Shadowing trong Rust cho phép bạn tái sử dụng tên biến với kiểu dữ liệu khác.

Chapter Complete!

Great job! Keep the momentum going.

Your progress0 of 19 chapters read
← → to navigate chapters
Built: 4/8/2026, 12:01:11 PM