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.
Mặc định, mọi biến trong Rust đều là bất biến (immutable). Đây là một trong những lựa chọn thiết kế giúp Rust viết code an toàn và dễ lập luận hơn. Khi một biến là bất biến, giá trị của nó không thể thay đổi sau khi được gán.
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.
Để làm cho một biến có thể thay đổi, bạn thêm từ khóa mut vào trước tên biến. Điều này làm rõ ý định và giúp người đọc code hiểu rằng giá trị của biến đó có thể thay đổi.
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}Rust khuyến khích bạn sử dụng biến bất biến theo mặc định và chỉ dùng mut khi thực sự cần thiết. Điều này giúp trình biên dịch tối ưu hóa code tốt hơn và giảm thiểu lỗi logic.
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.
Hằng số được khai báo bằng từ khóa const và luôn bất biến — bạn không thể dùng mut với hằng số. Hằng số phải có chú thích kiểu tường minh và chỉ có thể được gán bằng biểu thức hằng (không phải kết quả hàm hay giá trị tính toán tại runtime).
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 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.
Che khuất cho phép bạn khai báo một biến mới với cùng tên, che đi biến trước. Biến mới có thể có kiểu dữ liệu khác. Đây khác với mut vì bạn thực sự tạo ra một biến mới — biến cũ vẫn bất biến.
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}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).
Rust có nhiều kiểu số nguyên tích hợp sẵn. Mỗi kiểu có thể là có dấu (signed, bắt đầu bằng i — lưu cả số âm) hoặc không dấu (unsigned, bắt đầu bằng u — chỉ số dương), với các kích thước 8, 16, 32, 64, 128 bit, hoặc arch (phụ thuộc kiến trúc máy).
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}Khi không chắc nên dùng kiểu số nguyên nào, i32 thường là lựa chọn tốt — nó nhanh ngay cả trên hệ thống 64-bit. Dùng usize khi làm việc với chỉ số collection hoặc kích thước bộ nhớ.
Beyond integers, Rust has three other important primitive types: floating-point numbers, booleans, and Unicode characters.
Ngoài số nguyên, Rust còn có ba kiểu nguyên thủy quan trọng khác: số thực dấu phẩy động, kiểu boolean và kiểu ký tự Unicode.
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)12Kiểu char trong Rust đại diện cho một Unicode scalar value, không phải một byte như trong C. Mỗi char chiếm 4 byte và có thể chứa bất kỳ ký tự Unicode nào, bao gồm cả emoji và ký tự CJK.
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.
Tuple nhóm một số giá trị có kiểu khác nhau lại thành một kiểu duy nhất. Tuple có độ dài cố định: sau khi khai báo, bạn không thể thêm hay bớt phần tử. Tuple rất hữu ích để trả về nhiều giá trị từ một hàm.
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;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.
Mảng nhóm nhiều giá trị có cùng kiểu với độ dài cố định tại thời điểm biên dịch. Mảng được cấp phát trên stack, không phải heap. Dùng mảng khi bạn biết chắc số phần tử sẽ không thay đổi; dùng Vec khi cần mảng có thể co giãn.
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());Nếu bạn cố truy cập phần tử ngoài phạm vi mảng, Rust sẽ panic tại runtime thay vì tiếp tục với bộ nhớ không hợp lệ như C/C++. Rust cũng kiểm tra giới hạn tại thời điểm biên dịch khi có thể.
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 ->.
Hàm là nền tảng trong Rust. Từ khóa fn khai báo hàm. Rust dùng snake_case cho tên hàm và biến theo quy ước. Kiểu của mỗi tham số phải được khai báo tường minh. Kiểu trả về được chỉ định bằng ->.
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 ->Trong Rust, hàm có thể được định nghĩa ở bất kỳ đâu trong file — bạn không cần khai báo trước khi sử dụng như trong C. Trình biên dịch sẽ tìm thấy chúng trong toàn bộ module.
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.
Đây là một trong những điểm quan trọng nhất cần hiểu về Rust: sự phân biệt giữa câu lệnh (statements) và biểu thức (expressions). Câu lệnh thực hiện hành động và không trả về giá trị. Biểu thức tính toán và trả về một giá trị.
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 valueThêm dấu chấm phẩy vào cuối biểu thức biến nó thành câu lệnh, làm cho nó trả về () thay vì giá trị của biểu thức. Đây là lý do tại sao giá trị trả về của hàm thường là biểu thức cuối cùng không có dấu chấm phẩy.
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
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?
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.
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;
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ì?
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.