Go Zero to Hero - Bài 10: Toán tử số học, logic và bitwise
Trong Golang, hệ thống toán tử mang theo triết lý thực dụng và kỷ luật của Google: loại bỏ những cú pháp nhập nhằng gây lỗi, tối ưu hóa hiệu năng và bảo vệ an toàn cho hệ thống ở cấp độ phần cứng.
1. Toán tử số học (Arithmetic Operators): Cú tát vào thói quen C/C++
Go cung cấp các toán tử cơ bản giống hệt mọi ngôn ngữ khác: +, -, *, /, và % (chia lấy dư). Tuy nhiên, Go đã đưa ra một quyết định thiết kế gây "sốc" với những ai quen viết code kiểu one-liner (viết gộp 1 dòng) trong C++ hay Java.
Sự khác biệt cốt lõi của ++ và --:
Trong Go, phép tăng (++) và giảm (--) là một câu lệnh (Statement) độc lập, không phải là một biểu thức (Expression).
- Không có phép tiền tố (Pre-increment): Bạn không thể viết
++ahay--a. Ngôn ngữ Go cấm điều này. - Không được gán: Bạn không thể gán kết quả của phép tăng/giảm cho một biến khác, cũng không thể nhét nó vào trong hàm
fmt.Println().
func main() {
count := 10
// HỢP LỆ: Phải đứng độc lập trên 1 dòng
count++
// BÁO LỖI! (Syntax error)
// result := count++
// fmt.Println(count++)
// ++count
}
Tại sao Go làm vậy? Để triệt tiêu hoàn toàn sự nhập nhằng! Các lỗi logic khó tìm nhất trong C/C++ thường xuất phát từ việc lập trình viên nhầm lẫn giữa việc tăng giá trị trước hay sau khi tính toán. Go nói: "Muốn cộng 1 thì viết xuống một dòng riêng, đừng lười!"
2. Toán tử So sánh & Logic: Nguyên lý "Đoản mạch" (Short-circuit)
Toán tử so sánh (Relational Operators)
Bao gồm: == (bằng), != (khác), <, >, <=, >=. Kết quả luôn trả về kiểu bool (true hoặc false).
Lưu ý: Như đã nói ở các bài trước, bạn chỉ có thể so sánh hai biến cùng kiểu dữ liệu. Đừng cố so sánh
int32vớiint64nếu chưa ép kiểu.
Toán tử Logic (Logical Operators)
Bao gồm: && (AND - Và), || (OR - Hoặc), ! (NOT - Phủ định).
Tính năng "Short-circuit" (Đoản mạch) sinh tử:
Giống như các ngôn ngữ hiện đại, Go đánh giá toán tử logic từ trái sang phải và sẽ dừng lại ngay lập tức nếu kết quả đã được định đoạt.
- Với
A && B: NếuAlàfalse, Go sẽ bỏ qua hoàn toàn việc chạy biểu thứcB(vì False AND với gì cũng ra False). - With
A || B: NếuAlàtrue, Go cũng bỏ quaB(vì True OR với gì cũng ra True).
Ứng dụng thực chiến (Bảo vệ Crash Server):
// Giả sử config là một con trỏ (Pointer) có thể bị nil (null)
// Nhờ short-circuit, nếu config == nil, đoạn config.Status sẽ không bao giờ chạy
// Nếu không có short-circuit, server của bạn sẽ dính lỗi "Panic: nil pointer dereference" và sập ngay!
if config != nil && config.Status == "ACTIVE" {
fmt.Println("Hệ thống đang chạy...")
}
3. Toán tử Bitwise: Vũ khí bí mật của dân lập trình hệ thống
Nếu bạn chỉ code các web app tạo CRUD (Create, Read, Update, Delete) thông thường, bạn hiếm khi dùng Bitwise. Nhưng khi đụng đến hệ thống cấp thấp (IoT, AFC System, thiết bị nhúng), tối ưu hóa bộ nhớ cho Redis, hay phân quyền Unix, Bitwise là vua.
Bitwise thao tác trực tiếp lên các bit nhị phân (0 và 1) của dữ liệu.
Các toán tử cơ bản:
&(Bitwise AND)|(Bitwise OR)^(Bitwise XOR)<<(Dịch trái - Left shift): Dịch bit sang trái 1 vị trí tương đương với nhân 2.>>(Dịch phải - Right shift): Dịch bit sang phải 1 vị trí tương đương với chia 2.
Ví dụ sức mạnh của dịch bit (<<) kết hợp iota:
Đây là cách hệ thống thực tế dùng Bitwise để định nghĩa các cờ phân quyền (Flags/Permissions):
package main
import "fmt"
const (
Read = 1 << iota // 1 << 0 = 1 (Nhị phân: 0001)
Write // 1 << 1 = 2 (Nhị phân: 0010)
Delete // 1 << 2 = 4 (Nhị phân: 0100)
Admin // 1 << 3 = 8 (Nhị phân: 1000)
)
func main() {
// 1. Cấp quyền: Dùng phép OR (|) để gộp quyền
userRole := Read | Write // 0001 | 0010 = 0011 (Giá trị là 3)
// 2. Kiểm tra quyền: Dùng phép AND (&)
// Nếu (userRole & Write) == Write, tức là user có quyền Write
if userRole & Write != 0 {
fmt.Println("User được phép ghi dữ liệu")
}
}
Nhờ Bitwise, bạn có thể lưu hàng tá cấu hình True/False chỉ trong một con số nguyên (int) duy nhất, tiết kiệm cực nhiều tài nguyên so với việc lưu một mảng các chuỗi hay mảng boolean.
Toán tử độc quyền của Go: Xóa bit (Bit clear / AND NOT) &^
Trong Go có một toán tử khá lạ lẫm là &^. Chức năng của nó là: Dựa vào các bit 1 của toán hạng bên phải để xóa (đưa về 0) các bit tương ứng ở toán hạng bên trái.
// Giả sử userRole đang là 0011 (Có quyền Read và Write)
// Muốn tước quyền Write (0010) của user:
userRole = userRole &^ Write // Kết quả: 0001 (Chỉ còn quyền Read)
Tổng kết
Tóm lại những nguyên tắc thép về Toán tử trong Go:
- Số học: Không có
++a, vàa++phải đứng một mình một dòng, không được gán ghép lăng nhăng. - Logic: Luôn tận dụng nguyên lý Đoản mạch (Short-circuit) để viết code an toàn, tránh lỗi nil pointer.
- Bitwise: Là công cụ hoàn hảo để tối ưu hóa việc lưu trữ cấu hình/phân quyền (Permissions), kết hợp tuyệt vời với phép dịch bit
<<và bộ đếmiota. Đừng quên Go có toán tử độc quyền&^để xóa bit.
All Rights Reserved