Giao diện
🔄 Control Structures (Điều khiển luồng)
Go có cú pháp control flow đơn giản nhưng mạnh mẽ. Không có
while, không có do-while — chỉ có for. Không có ?: ternary — chỉ có if-else. 🔀 If-Else: The Go Idiom
Basic Syntax
go
if condition {
// true branch
} else if anotherCondition {
// alternative
} else {
// default
}⚡ Inline Declaration Idiom (Quan trọng!)
Đây là pattern bạn sẽ thấy hàng nghìn lần trong Go codebase:
go
// ✅ Go Idiom: Declare + Check in one line
if err := validateRequest(req); err != nil {
return fmt.Errorf("validation failed: %w", err)
}
// err KHÔNG tồn tại ở đây - scoped to if block!🎓 Professor Tom's Deep Dive: Variable Scoping
Tại sao pattern này powerful?
- Tight Scoping:
errchỉ tồn tại trong if block → không pollute namespace - Immediate Handling: Declare và check ngay lập tức → giảm bugs
- Readability: Cả operation và error handling cùng một chỗ
go
// ❌ Traditional way (sai Go idiom)
err := validateRequest(req)
if err != nil {
return err
}
// err vẫn visible ở đây - có thể reuse nhầm
// ✅ Go idiom
if err := validateRequest(req); err != nil {
return err
}
// Clean namespace - không có "orphan" err variableMultiple Returns Pattern
go
func processOrder(orderID int64) error {
// Pattern: resource acquisition + immediate check
if order, err := db.GetOrder(orderID); err != nil {
return fmt.Errorf("fetch order: %w", err)
} else if !order.IsValid() {
return ErrInvalidOrder
} else {
// order và err đều available trong else block
return processValidOrder(order)
}
}🔥 HPN's Pitfall
Shadowing trap với :=:
go
var err error
if result, err := someOperation(); err != nil { // ⚠️ err bị SHADOW
return err
}
fmt.Println(err) // err ở đây là outer err (nil), không phải inner err!Fix: Dùng = thay vì := khi muốn reuse variable:
go
var err error
var result Result
if result, err = someOperation(); err != nil {
return err
}🎛️ Switch-Case: Cleaner Branching
Basic Switch
go
func getStatusMessage(code int) string {
switch code {
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
default:
return "Unknown Status"
}
}Automatic break (Unlike C/Java!)
🎓 Key Difference from C/Java
Go tự động break sau mỗi case! Không cần viết break statement.
c
// C/Java - phải có break!
switch(code) {
case 1:
doSomething();
break; // Bắt buộc, nếu không sẽ fall-through
case 2:
doOther();
break;
}go
// Go - break tự động
switch code {
case 1:
doSomething() // Tự động break sau dòng này
case 2:
doOther()
}fallthrough Keyword
Khi bạn thực sự muốn fall-through (hiếm khi cần):
go
switch level {
case "admin":
grantAdminPrivileges()
fallthrough // Tiếp tục xuống case tiếp theo
case "moderator":
grantModeratorPrivileges()
fallthrough
case "user":
grantBasicPrivileges()
}
// admin sẽ có tất cả privileges, moderator có mod+user, user chỉ có basic⚡ Tagless Switch (Powerful!)
Thay thế long if-else chains:
go
// ❌ Hard to read với nhiều conditions
if score >= 90 {
grade = "A"
} else if score >= 80 {
grade = "B"
} else if score >= 70 {
grade = "C"
} else if score >= 60 {
grade = "D"
} else {
grade = "F"
}
// ✅ Tagless switch - sạch hơn nhiều
switch {
case score >= 90:
grade = "A"
case score >= 80:
grade = "B"
case score >= 70:
grade = "C"
case score >= 60:
grade = "D"
default:
grade = "F"
}Type Switch
go
func describe(i interface{}) string {
switch v := i.(type) {
case int:
return fmt.Sprintf("Integer: %d", v)
case string:
return fmt.Sprintf("String of length %d", len(v))
case bool:
return fmt.Sprintf("Boolean: %t", v)
case *User:
return fmt.Sprintf("User pointer: %s", v.Name)
default:
return fmt.Sprintf("Unknown type: %T", v)
}
}🔁 The for Loop: Go's Only Loop
Go chỉ có for. Không có while, không có do-while. Nhưng for có thể làm tất cả:
3 Forms of for
go
// Form 1: Traditional C-style
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// Form 2: While-style (chỉ condition)
for count < limit {
count++
}
// Form 3: Infinite loop
for {
if shouldStop() {
break
}
processNext()
}For-Range: Iterating Collections
go
// Slice/Array
users := []string{"Alice", "Bob", "Charlie"}
for index, name := range users {
fmt.Printf("%d: %s\n", index, name)
}
// Map
scores := map[string]int{"Alice": 95, "Bob": 87}
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
// String (iterates over runes, not bytes!)
for i, r := range "Việt" {
fmt.Printf("Position %d: %c\n", i, r)
}
// Channel
for msg := range messageChannel {
process(msg)
}🔥 HPN's Pitfall: For-Range Variable Capture Trap
Đây là bug KINH ĐIỂN trong Go! (Fixed trong Go 1.22, nhưng vẫn cần hiểu)
go
// ❌ Bug: Tất cả goroutines sẽ in "Charlie" (value cuối cùng)
users := []string{"Alice", "Bob", "Charlie"}
for _, name := range users {
go func() {
fmt.Println(name) // Capture biến 'name' - CÙNG MỘT BIẾN!
}()
}
time.Sleep(time.Second)
// Output: Charlie, Charlie, Charlie (không phải Alice, Bob, Charlie!)Lý do (Go < 1.22):
┌─────────────────────────────────────────────────────────────┐
│ FOR LOOP │
├─────────────────────────────────────────────────────────────┤
│ Iteration 1: name = "Alice" │
│ Iteration 2: name = "Bob" ← CÙNG biến 'name' bị reused │
│ Iteration 3: name = "Charlie" │
│ │
│ Tất cả closures capture POINTER đến biến 'name' │
│ Khi goroutines chạy, 'name' đã = "Charlie" │
└─────────────────────────────────────────────────────────────┘Fix (trước Go 1.22):
go
// Fix 1: Copy vào local variable
for _, name := range users {
name := name // Shadow với local copy
go func() {
fmt.Println(name) // Capture local copy
}()
}
// Fix 2: Pass as parameter
for _, name := range users {
go func(n string) {
fmt.Println(n)
}(name) // Pass by value
}Go 1.22+: Loop variables được tạo mới mỗi iteration → fix by default!
go
// Go 1.22+ - hoạt động đúng
for _, name := range users {
go func() {
fmt.Println(name) // Mỗi iteration có 'name' riêng
}()
}Performance: Range over Large Structs
go
type BigStruct struct {
Data [10000]byte
}
items := []BigStruct{{}, {}, {}}
// ❌ Bad: Copy 10KB mỗi iteration
for _, item := range items {
process(item)
}
// ✅ Good: Chỉ copy index (int)
for i := range items {
process(&items[i]) // Pass pointer, không copy
}🏷️ Labels & Goto
Go hỗ trợ goto và labels, nhưng sử dụng rất hạn chế:
go
// Breaking out of nested loops
OuterLoop:
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if shouldExit(i, j) {
break OuterLoop // Break cả 2 loops
}
}
}📌 HPN Standard: Labels & Goto
Quy tắc:
- Labels chỉ dùng để break/continue nested loops
- KHÔNG BAO GIỜ dùng
gotocho control flow logic - Nếu cần
goto, refactor code thành functions
go
// ❌ Never do this
func bad() {
if condition {
goto cleanup
}
// ...
cleanup:
// ...
}
// ✅ Do this instead
func good() {
defer cleanup()
if condition {
return
}
// ...
}🎮 Interactive: Scenario Choice
Bạn cần xử lý HTTP status codes (200, 201, 400, 401, 403, 404, 500, 502, 503). Code nào dễ maintain hơn?
📊 Summary: Control Flow Patterns
| Pattern | Use Case | Example |
|---|---|---|
| Inline if declaration | Error handling | if err := op(); err != nil |
| Tagless switch | Range conditions | switch { case x > 0: ... } |
| Type switch | Interface handling | switch v := i.(type) |
| For-range | Collection iteration | for _, v := range slice |
| Labeled break | Nested loop exit | break OuterLoop |
➡️ Tiếp theo
Control flow nắm vững rồi! Tiếp theo: Functions & Methods - Multiple returns, defer, closures, và variadic functions.