Giao diện
Functions & Control Flow Stack Deep Dive
Mỗi Function Call tạo Stack Frame — Hiểu cách data di chuyển
Function Syntax Basics
rust
fn add(x: i32, y: i32) -> i32 {
x + y // Expression - không cần 'return'
}
fn main() {
let result = add(5, 3);
println!("Result: {}", result);
}Quan sát:
- Parameters phải có type annotations (
x: i32) - Return type sau
->(bỏ qua nếu trả về()) - Không cần
returncho expression cuối cùng
Stack Frames: Anatomy of a Function Call
Mỗi function call tạo Stack Frame
rust
fn multiply(a: i32, b: i32) -> i32 {
let result = a * b;
result
}
fn main() {
let x = 5;
let y = 3;
let z = multiply(x, y);
}Stack Evolution During Execution
═══════════════════════════════════════════════════════════════════
STEP 1: main() được gọi
═══════════════════════════════════════════════════════════════════
┌─────────────────────────────────┐ ← Stack Top
│ STACK FRAME: main() │
├─────────────────────────────────┤
│ Return Address (to OS/runtime) │
├─────────────────────────────────┤
│ x: i32 = 5 │
├─────────────────────────────────┤
│ y: i32 = 3 │
├─────────────────────────────────┤
│ z: i32 = ??? (uninitialized) │
└─────────────────────────────────┘ ← Stack Base
═══════════════════════════════════════════════════════════════════
STEP 2: multiply(x, y) được gọi
═══════════════════════════════════════════════════════════════════
┌─────────────────────────────────┐ ← Stack Top
│ STACK FRAME: multiply() │
├─────────────────────────────────┤
│ Return Address (to main) │
├─────────────────────────────────┤
│ a: i32 = 5 (copy of x) │
├─────────────────────────────────┤
│ b: i32 = 3 (copy of y) │
├─────────────────────────────────┤
│ result: i32 = 15 │
├═════════════════════════════════┤
│ STACK FRAME: main() │
├─────────────────────────────────┤
│ Return Address │
├─────────────────────────────────┤
│ x: i32 = 5 │
├─────────────────────────────────┤
│ y: i32 = 3 │
├─────────────────────────────────┤
│ z: i32 = ??? │
└─────────────────────────────────┘ ← Stack Base
═══════════════════════════════════════════════════════════════════
STEP 3: multiply() returns 15
═══════════════════════════════════════════════════════════════════
Return value (15) được copy về qua register (thường RAX)
┌───────────┐
Stack Frame: │ RAX = 15 │ ← Return value
multiply() └─────┬─────┘
[DEALLOCATED] │
↓ │
┌────────────────────────────────┐
│ STACK FRAME: main() │
├────────────────────────────────┤
│ x: i32 = 5 │
├────────────────────────────────┤
│ y: i32 = 3 │
├────────────────────────────────┤
│ z: i32 = 15 ←────────────────┘ (từ RAX)
└────────────────────────────────┘Key Insights
- Arguments được COPY vào stack frame mới (cho
Copytypes nhưi32) - Return value qua register (RAX on x86_64) hoặc memory nếu lớn
- Stack frame bị deallocate ngay khi function return
- LIFO order: Last In, First Out
Statements vs Expressions
Fundamental Difference
| Concept | Definition | Returns Value? | Example |
|---|---|---|---|
| Statement | Thực hiện action | ❌ Không (()) | let x = 5; |
| Expression | Tính toán giá trị | ✅ Có | 5 + 3, { x + 1 } |
Statements
rust
fn main() {
let x = 5; // Statement: let binding
let y = { // Statement: let với block expression
let z = 3; // Statement bên trong
z + 1 // Expression - giá trị của block
};
// ❌ KHÔNG COMPILE:
// let a = (let b = 5); // `let` là statement, không có value
}Expressions are Everywhere
rust
fn main() {
// Số, operators là expressions
let a = 5; // 5 là expression
let b = 5 + 3; // 5 + 3 là expression
// Blocks {} là expressions
let c = {
let x = 10;
x * 2 // KHÔNG có semicolon → đây là return value của block
}; // c = 20
// if/else là expression
let d = if true { 1 } else { 0 }; // d = 1
// match là expression
let e = match 5 {
0 => "zero",
_ => "non-zero",
}; // e = "non-zero"
// Function call là expression
let f = add(1, 2); // f = 3
}
fn add(x: i32, y: i32) -> i32 {
x + y // Expression, KHÔNG có semicolon
}Semicolon Rules
rust
fn returns_five() -> i32 {
5 // ✅ Expression - returns 5
}
fn returns_unit() -> () {
5; // Statement - returns () vì có semicolon
}
// ❌ KHÔNG COMPILE:
// fn broken() -> i32 {
// 5; // Error: expected `i32`, found `()`
// }Memory View:
returns_five(): returns_unit():
┌────────────────┐ ┌────────────────┐
│ Expression: 5 │───→ RAX │ Statement: 5; │───→ () (nothing)
│ (no semicolon)│ │ (semicolon) │
└────────────────┘ └────────────────┘
Returns i32 Returns ()Control Flow: if Expression
if trong Rust là Expression
rust
fn main() {
let condition = true;
// Traditional (statement-like) usage
if condition {
println!("true branch");
} else {
println!("false branch");
}
// Expression usage (giống ternary trong C)
let number = if condition { 5 } else { 6 };
// ❌ KHÔNG COMPILE: Type mismatch
// let bad = if condition { 5 } else { "six" };
}Tại sao branches phải cùng type?
if condition { 5 } else { 6 }
↓ ↓
i32 i32 ✅ OK
if condition { 5 } else { "six" }
↓ ↓
i32 &str ❌ Compiler Error!Compiler cần biết type của expression tại compile time để allocate stack space.
Control Flow: Loops
loop — Infinite Loop with Return
rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // Return value từ loop
}
};
println!("Result: {}", result); // 20
}while — Conditional Loop
rust
fn main() {
let mut n = 3;
while n > 0 {
println!("{}!", n);
n -= 1;
}
println!("LIFTOFF!");
}for — Iterator Loop
rust
fn main() {
let arr = [10, 20, 30, 40, 50];
// Iterate by reference (không move ownership)
for element in arr.iter() {
println!("{}", element);
}
// Range syntax
for i in 0..5 {
println!("Index: {}", i);
}
// Enumerate
for (index, value) in arr.iter().enumerate() {
println!("arr[{}] = {}", index, value);
}
}Stack allocation cho loop:
for i in 0..5 { ... }
Loop iteration 0:
┌────────────────┐
│ i: usize = 0 │
└────────────────┘
Loop iteration 1:
┌────────────────┐
│ i: usize = 1 │ ← Same stack slot, overwritten
└────────────────┘
...match — Pattern Matching (An toàn hơn switch-case)
Cú pháp cơ bản
rust
fn main() {
let number = 3;
match number {
1 => println!("One"),
2 => println!("Two"),
3 => println!("Three"),
_ => println!("Something else"), // Wildcard - catch-all
}
}Tại sao match an toàn hơn switch-case?
1. Exhaustive Matching — Compiler bắt buộc handle ALL cases
rust
enum Direction {
North,
South,
East,
West,
}
fn describe(dir: Direction) -> &'static str {
match dir {
Direction::North => "Going north",
Direction::South => "Going south",
Direction::East => "Going east",
// ❌ KHÔNG COMPILE: missing `Direction::West`
}
}
// ✅ ĐÚNG: Handle tất cả variants
fn describe_complete(dir: Direction) -> &'static str {
match dir {
Direction::North => "Going north",
Direction::South => "Going south",
Direction::East => "Going east",
Direction::West => "Going west",
}
}So sánh với C:
c
// C switch - KHÔNG có exhaustive checking
switch (dir) {
case NORTH: return "North";
case SOUTH: return "South";
// Quên EAST và WEST → undefined behavior hoặc fallthrough
}2. No Fallthrough — Mỗi arm tách biệt
rust
fn main() {
let x = 1;
match x {
1 => println!("One"),
2 => println!("Two"), // KHÔNG fallthrough từ 1
_ => println!("Other"),
}
}So sánh với C:
c
switch (x) {
case 1:
printf("One");
// BUG: quên break → fallthrough sang case 2!
case 2:
printf("Two");
break;
}3. Pattern Destructuring — Extract values
rust
fn main() {
let point = (3, 5);
match point {
(0, 0) => println!("Origin"),
(x, 0) => println!("On x-axis at {}", x),
(0, y) => println!("On y-axis at {}", y),
(x, y) => println!("At ({}, {})", x, y),
}
}4. Guards — Extra conditions
rust
fn main() {
let num = Some(4);
match num {
Some(x) if x < 5 => println!("Less than five: {}", x),
Some(x) => println!("Greater or equal to five: {}", x),
None => println!("No value"),
}
}match với Ranges
rust
fn main() {
let age: u8 = 25;
match age {
0..=12 => println!("Child"),
13..=19 => println!("Teenager"),
20..=59 => println!("Adult"),
60..=u8::MAX => println!("Senior"),
}
}match là Expression
rust
fn main() {
let x = 5;
let description = match x {
1 => "one",
2 => "two",
3 => "three",
_ => "many",
};
println!("{}", description); // "many"
}Stack Frame với Control Flow
Nested Scopes
rust
fn main() {
let a = 5;
{
let b = 10;
{
let c = 15;
println!("{} {} {}", a, b, c);
} // c dropped here
} // b dropped here
} // a dropped hereStack Evolution:
Bước 1: let a = 5
┌─────────────────┐
│ a: i32 = 5 │
└─────────────────┘
Bước 2: Enter inner scope, let b = 10
┌─────────────────┐
│ b: i32 = 10 │ ← Inner scope
├─────────────────┤
│ a: i32 = 5 │
└─────────────────┘
Bước 3: Enter innermost scope, let c = 15
┌─────────────────┐
│ c: i32 = 15 │ ← Innermost scope
├─────────────────┤
│ b: i32 = 10 │
├─────────────────┤
│ a: i32 = 5 │
└─────────────────┘
Bước 4: Exit innermost scope (c dropped)
┌─────────────────┐
│ b: i32 = 10 │
├─────────────────┤
│ a: i32 = 5 │
└─────────────────┘
Bước 5: Exit inner scope (b dropped)
┌─────────────────┐
│ a: i32 = 5 │
└─────────────────┘Function Parameters: Move vs Copy
Copy Types (primitives)
rust
fn takes_i32(x: i32) {
println!("{}", x);
}
fn main() {
let num = 42;
takes_i32(num); // num được COPY vào function
println!("{}", num); // ✅ num vẫn valid
}Move Types (heap-allocated)
rust
fn takes_string(s: String) {
println!("{}", s);
} // s dropped here
fn main() {
let hello = String::from("hello");
takes_string(hello); // hello được MOVE vào function
// println!("{}", hello); // ❌ ERROR: value borrowed after move
}Stack visualization:
MOVE với String:
main() TRƯỚC call: takes_string() stack frame:
┌─────────────────────┐ ┌─────────────────────┐
│ hello: String │ → │ s: String │
│ ptr ──────────────────────→ │ ptr ──────────────────→ "hello" (heap)
│ len: 5 │ │ len: 5 │
│ cap: 5 │ │ cap: 5 │
└─────────────────────┘ └─────────────────────┘
[invalidated] [owns heap data]Bảng Tóm tắt
| Concept | Key Point |
|---|---|
| Stack Frame | Được tạo mỗi function call, chứa locals và return address |
| Expression | Tính toán và trả về value, KHÔNG có semicolon cuối |
| Statement | Thực hiện action, có semicolon, trả về () |
if is expression | Branches phải cùng type |
match exhaustive | Compiler bắt buộc handle ALL cases |
match no fallthrough | Mỗi arm tách biệt, không cần break |
| Move vs Copy | Primitives copy, heap types move |
Bài tập kiểm tra
💪 Bài 1: Expression hay Statement?
rust
let x = 5; // A
5 + 3 // B
{ let y = 1; } // C
{ let y = 1; y } // DĐáp án
- A: Statement (let binding)
- B: Expression (arithmetic)
- C: Expression trả về
()(block có statement bên trong, không có expression cuối) - D: Expression trả về
i32(block có expression cuốiy)
💪 Bài 2: Fix lỗi compile
rust
fn main() {
let result = if true {
5
} else {
"five"
};
}Đáp án
Branches phải cùng type:
rust
// Cách 1: Cả hai trả về i32
let result = if true { 5 } else { 0 };
// Cách 2: Cả hai trả về &str
let result = if true { "5" } else { "five" };
// Cách 3: Generic approach (không khuyến khích)
// Dùng enum hoặc trait object