Biến trong Go
Biến là vị trí lưu trữ dùng để lưu một giá trị, cho phép giá trị lưu trữ của nó thay đổi động trong thời gian chạy. Mỗi khi khai báo một biến, sẽ phân bổ một vùng bộ nhớ để lưu trữ giá trị của kiểu tương ứng, đến Sổ tay tham khảo - Biến để xem thêm chi tiết.
Khai báo
Trong Go việc khai báo kiểu là hậu trí, việc khai báo biến sẽ dùng đến từ khóa var, định dạng là var tên_biến tên_kiểu, quy tắc đặt tên biến phải tuân theo quy tắc đặt tên định danh.
var intNum int
var str string
var char byteKhi muốn khai báo nhiều biến cùng kiểu, có thể chỉ viết kiểu một lần
var numA, numB, numC intKhi muốn khai báo nhiều biến khác kiểu, có thể sử dụng () để bao bọc, có thể tồn tại nhiều ().
var (
name string
age int
address string
)
var (
school string
class int
)Một biến nếu chỉ khai báo mà không gán giá trị, thì giá trị lưu trữ của biến là giá trị 0 của kiểu tương ứng.
Gán giá trị
Việc gán giá trị sẽ dùng đến toán tử =, ví dụ
var name string
name = "jack"Cũng có thể gán giá trị trực tiếp khi khai báo
var name string = "jack"Hoặc như vậy cũng được
var name string
var age int
name, age = "jack", 1Cách thứ hai mỗi lần đều phải chỉ định kiểu, có thể sử dụng đường cú pháp do chính thức cung cấp: khởi tạo biến ngắn, có thể bỏ qua từ khóa var và kiểu hậu trí, cụ thể là kiểu gì giao cho trình biên dịch tự suy luận.
name := "jack" // Biến kiểu chuỗi.Tuy có thể không cần chỉ định kiểu, nhưng khi gán giá trị sau này, kiểu phải giữ nhất quán, đoạn mã như dưới đây không thể biên dịch.
a := 1
a = "1"Còn cần lưu ý là, khởi tạo biến ngắn không thể sử dụng nil, vì nil không thuộc bất kỳ kiểu nào, trình biên dịch không thể suy luận kiểu của nó.
name := nil // Không thể biên dịchKhai báo biến ngắn có thể khởi tạo hàng loạt
name, age := "jack", 1Cách khai báo biến ngắn không thể sử dụng cho một biến đã tồn tại, ví dụ
// Ví dụ sai
var a int
a := 1
// Ví dụ sai
a := 1
a := 2Nhưng có một trường hợp ngoại lệ, đó là khi gán giá trị biến cũ đồng thời khai báo một biến mới, ví dụ
a := 1
a, b := 2, 2Đoạn mã như vậy có thể biên dịch, biến a được gán giá trị lại, còn b là khai báo mới.
Trong ngôn ngữ Go, có một quy tắc, đó là tất cả các biến trong hàm đều phải được sử dụng, ví dụ đoạn mã dưới đây chỉ khai báo biến, nhưng không sử dụng nó
func main() {
a := 1
}Thì khi biên dịch sẽ báo lỗi, nhắc nhở bạn biến này đã khai báo nhưng không sử dụng
a declared and not usedQuy tắc này chỉ áp dụng cho biến trong hàm, đối với biến cấp gói bên ngoài hàm thì không có hạn chế này, đoạn mã dưới đây có thể biên dịch.
var a = 1
func main() {
}Ẩn danh
Dùng dấu gạch dưới có thể biểu thị không cần một biến nào đó
Open(name string) (*File, error)Ví dụ hàm os.Open có hai giá trị trả về, chúng ta chỉ muốn lấy cái đầu tiên, không muốn cái thứ hai, có thể viết như sau
file, _ := os.Open("readme.txt")Biến không được sử dụng là không thể biên dịch, khi bạn không cần một biến nào đó, có thể sử dụng dấu gạch dưới _ để thay thế.
Hoán đổi
Trong Go, nếu muốn hoán đổi giá trị của hai biến, không cần sử dụng con trỏ, có thể sử dụng toán tử gán để trực tiếp hoán đổi, về cú pháp trông rất trực quan, ví dụ như sau
num1, num2 := 25, 36
num1, num2 = num2, num1Ba biến cũng như vậy
num1, num2, num3 := 25, 36, 49
num1, num2, num3 = num3, num2, num1Suy nghĩ đoạn mã dưới đây, đây là một đoạn mã nhỏ tính dãy số Fibonacci, giá trị của ba biến sau khi tính toán lần lượt là gì
a, b, c := 0, 1, 1
a, b, c = b, c, a+bĐáp án là
1 1 1Bạn có thể nghi ngờ tại sao không phải là
1 1 2Rõ ràng a đã được gán giá trị của b, tại sao kết quả của a+b vẫn là 1? Go khi thực hiện phép toán gán nhiều biến, thứ tự của nó là trước tính giá trị rồi gán, không phải tính từ trái sang phải.
a, b, c = b, c, a+bBạn có thể cho rằng nó sẽ được triển khai thành đoạn dưới đây
a = b
b = c
c = a + bNhưng thực tế nó sẽ tính toán giá trị của ba số a, b, c rồi gán cho chúng, cũng giống như đoạn mã dưới đây
a, b, c = 1, 1, 0+1Khi liên quan đến gọi hàm, hiệu quả này càng rõ ràng hơn, chúng ta có một hàm sum có thể tính giá trị trả về của hai số
func sum(a, b int) int {
return a + b
}Thông qua hàm để thực hiện hai số cộng nhau
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)Kết quả không thay đổi, khi tính giá trị trả về của hàm sum, tham số đầu vào của nó vẫn là 0 và 1
1 1 1Nên đoạn mã nên viết tách ra như vậy.
a, b = b, c
c = a + bSo sánh
Việc so sánh giữa các biến có một tiền đề lớn, đó là kiểu giữa chúng phải giống nhau, trong ngôn ngữ Go không tồn tại chuyển đổi kiểu ngầm, đoạn mã như dưới đây không thể biên dịch
func main() {
var a uint64
var b int64
fmt.Println(a == b)
}Trình biên dịch sẽ nói với bạn kiểu giữa hai cái không giống nhau
invalid operation: a == b (mismatched types uint64 and int64)Nên phải sử dụng chuyển đổi kiểu bắt buộc
func main() {
var a uint64
var b int64
fmt.Println(int64(a) == b)
}Trước khi có泛型, hàm tích hợp min, max do Go cung cấp ban đầu chỉ hỗ trợ số dấu phẩy động, đến phiên bản 1.21, Go cuối cùng đã viết lại hai hàm tích hợp này bằng泛型, hiện tại có thể sử dụng hàm min để so sánh giá trị nhỏ nhất
minVal := min(1, 2, -1, 1.2)Sử dụng hàm max để so sánh giá trị lớn nhất
maxVal := max(100, 22, -1, 1.12)Tham số của chúng hỗ trợ tất cả các kiểu có thể so sánh, kiểu có thể so sánh trong Go có
- Boolean
- Số
- Chuỗi
- Con trỏ
- Kênh (chỉ hỗ trợ phán đoán có bằng nhau hay không)
- Mảng có kiểu phần tử là kiểu có thể so sánh (slice không thể so sánh) (chỉ hỗ trợ phán đoán có bằng nhau hay không) (chỉ hỗ trợ so sánh giữa các mảng cùng độ dài, vì độ dài mảng cũng là một phần của kiểu, và kiểu khác nhau không thể so sánh)
- Struct có kiểu trường đều là kiểu có thể so sánh (chỉ hỗ trợ phán đoán có bằng nhau hay không)
Ngoài ra, cũng có thể thông qua nhập thư viện chuẩn cmp để phán đoán, nhưng chỉ hỗ trợ tham số kiểu có thứ tự, trong Go kiểu có thứ tự tích hợp chỉ có số và chuỗi.
import "cmp"
func main() {
cmp.Compare(1, 2)
cmp.Less(1, 2)
}Khối mã
Bên trong hàm, có thể thông qua dấu ngoặc nhọn để xây dựng một khối mã, phạm vi biến giữa các khối mã độc lập với nhau. Ví dụ đoạn mã dưới đây
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
a := 3
fmt.Println(a)
}
fmt.Println(a)
}Kết quả xuất của nó là
2
3
1Biến giữa các khối độc lập với nhau, không bị ảnh hưởng, không thể truy cập, nhưng sẽ bị ảnh hưởng bởi khối cha.
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
fmt.Println(a)
}
fmt.Println(a)
}Kết quả xuất của nó là
2
1
1