Variables
1. Tổng quan về Variables trong Go
Biến trong Go là một cách để lưu trữ dữ liệu trong bộ nhớ, được đặt tên và có kiểu dữ liệu cụ thể. Go là một ngôn ngữ statically typed (kiểu tĩnh), nghĩa là kiểu của biến phải được xác định tại thời điểm biên dịch và không thể thay đổi trong thời gian chạy.
Các đặc điểm chính của biến trong Go:
Khai báo rõ ràng: Bạn phải khai báo biến với kiểu hoặc để Go suy ra kiểu (type inference).
Zero value: Nếu biến không được gán giá trị ban đầu, Go tự động gán giá trị mặc định (zero value) dựa trên kiểu.
Scope: Biến có phạm vi rõ ràng (package-level, function-level, block-level).
Immutability: Go không có từ khóa
const
cho biến thông thường, nhưng bạn có thể dùngconst
cho hằng số.
2. Cách khai báo biến trong Go
Go cung cấp nhiều cách để khai báo biến, và mỗi cách có ngữ cảnh sử dụng riêng.
2.1. Sử dụng từ khóa var
Cú pháp:
Ví dụ:
Zero value:
int
,float64
, ...:0
string
:""
(chuỗi rỗng)bool
:false
pointer
,slice
,map
,channel
,interface
:nil
2.2. Khai báo ngắn gọn với :=
(Short Variable Declaration)
Cú pháp:
Ví dụ:
Lưu ý:
Chỉ sử dụng
:=
trong các block scope (ví dụ: trong hàm).Không sử dụng
:=
ở package-level.:=
yêu cầu gán giá trị ban đầu.Nếu biến đã tồn tại,
:=
sẽ báo lỗi. Thay vào đó, dùng=
để gán lại giá trị.
2.3. Khai báo nhiều biến cùng lúc
Go cho phép khai báo nhiều biến trong một dòng, rất tiện lợi.
Ví dụ:
Hoặc với short declaration:
2.4. Khai báo ở Package-level
Biến ở cấp package (ngoài hàm) phải dùng var
, không thể dùng :=
.
Ví dụ:
3. Kiểu dữ liệu và Type Inference
Go hỗ trợ nhiều kiểu dữ liệu cơ bản (int
, string
, bool
, float64
, v.v.) và kiểu phức hợp (slice
, map
, struct
, v.v.). Khi khai báo biến, bạn có thể chỉ định kiểu rõ ràng hoặc để Go suy ra kiểu.
Ví dụ về type inference:
Chuyên sâu:
Type inference không phải là "đoán mò". Go xác định kiểu dựa trên giá trị ban đầu tại thời điểm biên dịch.
Nếu bạn cần kiểm soát kiểu chính xác (ví dụ:
int32
thay vìint
), hãy khai báo kiểu rõ ràng:
4. Scope và Lifetime của biến
4.1. Scope
Package-level: Biến khai báo ngoài hàm có phạm vi toàn package. Có thể được truy cập từ bất kỳ hàm nào trong package (hoặc từ package khác nếu được export - tên bắt đầu bằng chữ in hoa).
Function-level: Biến khai báo trong hàm chỉ tồn tại trong hàm đó.
Block-level: Biến khai báo trong một block (ví dụ:
if
,for
) chỉ tồn tại trong block đó.
Ví dụ:
4.2. Lifetime
Biến tồn tại trong suốt thời gian scope của nó còn hoạt động.
Biến ở package-level tồn tại suốt vòng đời của chương trình.
Biến trong hàm/block được garbage collector dọn dẹp khi hàm/block kết thúc.
Chuyên sâu:
Go không cho phép shadowing biến trong cùng block nếu không cần thiết. Ví dụ:
Tuy nhiên, shadowing được phép trong các block con:
5. Hằng số (Constants)
Hằng số trong Go không phải là biến thông thường, nhưng liên quan mật thiết. Hằng số được khai báo bằng const
và có giá trị không thể thay đổi.
Ví dụ:
Chuyên sâu:
Hằng số có thể là untyped (không có kiểu cụ thể) cho đến khi được sử dụng:
Hằng số chỉ hỗ trợ các kiểu cơ bản (
int
,float
,string
,bool
).Go hỗ trợ constant expressions được tính toán tại thời điểm biên dịch:
6. Best Practices khi làm việc với biến
Một Senior Golang cần hiểu và áp dụng các best practices để viết code sạch và hiệu quả:
Ưu tiên khai báo ngắn gọn (
:=
) trong hàm:Giảm boilerplate, tăng tính đọc.
Ví dụ:
x := 42
thay vìvar x int = 42
.
Khai báo kiểu rõ ràng khi cần thiết:
Khi bạn muốn đảm bảo kiểu chính xác (ví dụ:
int32
thay vìint
).Ví dụ:
var x int32 = 42
.
Tránh shadowing không cần thiết:
Shadowing có thể gây nhầm lẫn. Hãy kiểm tra kỹ các biến trong block con.
Sử dụng zero value hợp lý:
Tận dụng zero value thay vì gán giá trị mặc định không cần thiết.
Ví dụ: Không cần
var x int = 0
, vìvar x int
đã là0
.
Đặt tên biến rõ ràng:
Tên biến nên ngắn gọn nhưng mô tả ý nghĩa. Ví dụ:
userCount
thay vìuc
.Biến package-level nên có tên exported (bắt đầu bằng chữ in hoa) nếu cần truy cập từ package khác.
Hạn chế biến toàn cục (global variables):
Biến package-level có thể gây khó kiểm soát trạng thái. Thay vào đó, truyền biến qua tham số hàm.
Kiểm tra lỗi khi gán nhiều giá trị:
Khi dùng
:=
với nhiều giá trị, đảm bảo xử lý lỗi đúng cách:
7. Các chi tiết nâng cao
7.1. Memory Allocation
Biến trong Go được cấp phát trên stack hoặc heap tùy thuộc vào cách sử dụng.
Biến cục bộ (local) thường ở stack, nhưng nếu chúng "escape" (ví dụ: được tham chiếu bởi con trỏ hoặc trả về từ hàm), chúng sẽ được cấp phát trên heap.
Công cụ
go build -gcflags '-m'
có thể dùng để phân tích escape analysis:
7.2. Type Conversion
Go không tự động chuyển đổi kiểu, bạn phải ép kiểu rõ ràng:
7.3. Unused Variables
Go không cho phép biến khai báo mà không sử dụng (lỗi biên dịch). Để tạm thời bỏ qua, dùng
_
:
7.4. Concurrency và Variables
Khi làm việc với goroutines, biến cần được quản lý cẩn thận để tránh data race.
Dùng
sync.Mutex
hoặc channel để đồng bộ:
8. Ví dụ thực tế
Dưới đây là một ví dụ kết hợp các khái niệm về biến trong một chương trình thực tế:
9. Kết luận
Hiểu sâu về biến trong Go không chỉ là nắm cú pháp mà còn là biết cách sử dụng chúng hiệu quả trong các ngữ cảnh thực tế, từ quản lý bộ nhớ, tối ưu hiệu suất, đến xử lý concurrency. Một Senior Golang sẽ luôn cân nhắc scope, type safety, và best practices để viết code rõ ràng, dễ bảo trì, và ít lỗi.
Last updated