Go Zero to Hero - Bài 12: Cấu trúc rẽ nhánh: switch-case và từ khoá fallthrough
Trong các ngôn ngữ như C++ hay Java, việc quên từ khóa break ở cuối mỗi case thường là nguyên nhân gây ra những lỗi logic "dở khóc dở cười" do luồng chạy tràn (fall-through). Trong Go, tư duy thiết kế đã thay đổi hoàn toàn: sự an toàn được đặt lên hàng đầu.
1. Sự "tuyệt chủng" của từ khóa break ngầm định
Trong Go, những nhà thiết kế ngôn ngữ đã quyết định: Go sẽ tự động break khi kết thúc một khối case!
Hãy xem ví dụ về việc xử lý mã lỗi từ hệ thống bán vé tự động (AFC):
package main
import "fmt"
func main() {
ticketStatus := "PAID"
switch ticketStatus {
case "PENDING":
fmt.Println("Vé đang chờ thanh toán.")
// KHÔNG cần gõ break ở đây, Go sẽ tự thoát switch.
case "PAID":
fmt.Println("Thanh toán thành công. Mở cổng qua cửa!")
case "CANCELLED":
fmt.Println("Vé đã bị hủy.")
default:
fmt.Println("Trạng thái không hợp lệ.")
}
}
Mã code trên vừa ngắn gọn, vừa an toàn tuyệt đối. Khối default sẽ được chạy nếu không có bất kỳ case nào khớp (tương tự như else cuối cùng).
2. Những "đặc sản" nâng cấp của switch trong Go
Không chỉ an toàn hơn, switch của Go còn được bổ sung thêm nhiều sức mạnh đáng gờm so với các ngôn ngữ tiền bối.
Gộp nhiều giá trị vào một case
Bạn không cần phải viết lặp lại các case có chung cách xử lý. Chỉ cần dùng dấu phẩy , để gộp chúng lại.
errorCode := 404
switch errorCode {
case 400, 401, 403, 404:
fmt.Println("Lỗi từ phía Client (Người dùng gửi sai request).")
case 500, 502, 503:
fmt.Println("Lỗi từ phía Server (Hệ thống nội bộ đang gặp sự cố).")
}
Init Statement (Giống hệt if)
Giống như if, bạn có thể khởi tạo một biến dùng một lần ngay bên trong dòng khai báo switch. Biến này sẽ bị hủy ngay khi khối switch kết thúc.
// Gọi hàm checkHardware() và gán kết quả vào biến status ngay lập tức
switch status := checkHardware(); status {
case "OK":
fmt.Println("Thiết bị đọc thẻ hoạt động bình thường.")
case "ERROR":
fmt.Println("Lỗi phần cứng, cần bảo trì.")
}
// Tới đây biến status đã không còn tồn tại trong bộ nhớ.
Switch không điều kiện (Conditionless Switch)
Đây là kỹ thuật tuyệt vời để thay thế cho một chuỗi if-else if dài dòng. Nếu bạn bỏ trống phần biến sau từ khóa switch, Go sẽ ngầm hiểu đây là switch true. Các case lúc này không phải so sánh giá trị cố định nữa, mà có thể chứa các biểu thức logic phức tạp.
latency := 150 // Độ trễ tính bằng ms
switch {
case latency < 50:
fmt.Println("Kết nối cực nhanh (Ping xanh).")
case latency >= 50 && latency < 200:
fmt.Println("Kết nối ổn định (Ping vàng).")
default:
fmt.Println("Độ trễ quá cao (Ping đỏ), cảnh báo timeout!")
}
3. Từ khóa fallthrough: Cú quay xe có chủ đích
Như đã nói ở phần 1, Go tự động ngắt (break) ở cuối mỗi case. Vậy nếu trong một số ngữ cảnh cực kỳ hiếm hoi, bạn thực sự muốn luồng xử lý chạy tràn xuống case tiếp theo thì sao?
Đó là lúc bạn dùng từ khóa fallthrough.
Khi Go gặp fallthrough ở cuối một case, nó sẽ bỏ qua việc kiểm tra điều kiện của case liền kề bên dưới và nhảy thẳng vào thực thi luôn khối lệnh của case đó.
func main() {
processStep := 1
fmt.Println("Bắt đầu quy trình khởi động hệ thống:")
switch processStep {
case 1:
fmt.Println("Bước 1: Nạp file cấu hình.")
fallthrough // Bắt buộc chạy tiếp Bước 2
case 2:
fmt.Println("Bước 2: Kết nối Database (MySQL/PostgreSQL).")
fallthrough // Bắt buộc chạy tiếp Bước 3
case 3:
fmt.Println("Bước 3: Khởi động HTTP Server.")
case 4:
fmt.Println("Bước 4: Báo cáo metrics (Prometheus).")
}
}
Kết quả in ra màn hình:
Bắt đầu quy trình khởi động hệ thống:
Bước 1: Nạp file cấu hình.
Bước 2: Kết nối Database (MySQL/PostgreSQL).
Bước 3: Khởi động HTTP Server.
Lưu ý:
fallthroughchỉ có tác dụng thả trôi đúng 1 bậc (nhảy xuống case ngay dưới nó). Do ở case 3 không cófallthrough, quá trình thực thi sẽ dừng lại và không chạy case 4.
Tổng kết
Công lực của switch-case trong Go có thể tóm gọn qua 3 điểm cốt lõi:
- An toàn mặc định: Không cần viết
break, Go tự động ngắt block để ngăn lỗi chạy tràn. - Linh hoạt tối đa: Hỗ trợ gộp case (dấu phẩy), khai báo biến cục bộ (init statement), và dùng thay thế cho chuỗi
if-elsephức tạp (switch không điều kiện). - Kiểm soát luồng thủ công: Dùng
fallthroughkhi bạn thực sự muốn logic tràn xuống bước tiếp theo.
All Rights Reserved