☰ Mục lục series
Tài liệu Go tiếng Việt
  1. Bắt đầu (Getting started)
    1. Tổng quan
    2. Tải về và cài đặt
    3. Bắt đầu với Go
    4. Quản lý các bản cài đặt Go
    5. Plugin trình soạn thảo và IDE
  2. Dạo quanh Go (A Tour of Go)
    1. Chào mừng
    2. Package, biến và hàm
    3. Điều khiển luồng
    4. Struct, slice và map
    5. Method và interface
    6. Generics
    7. Xử lý đồng thời
  3. Hướng dẫn (Tutorials)
    1. Danh sách hướng dẫn
    2. Tạo một Go module
    3. Gọi mã của bạn từ một module khác
    4. Trả về và xử lý lỗi
    5. Trả về lời chào ngẫu nhiên
    6. Trả về lời chào cho nhiều người
    7. Thêm một test
    8. Biên dịch và cài đặt ứng dụng
    9. Kết luận
    10. Bắt đầu với workspace nhiều module
    11. Phát triển RESTful API với Go và Gin
    12. Truy cập cơ sở dữ liệu quan hệ
    13. Bắt đầu với generics
    14. Bắt đầu với fuzzing
    15. Làm việc với JSON
    16. Tìm và sửa các dependency có lỗ hổng với govulncheck
    17. Tìm và khắc phục phụ thuộc có lỗ hổng với VS Code Go
    18. Viết ứng dụng web
  4. Viết code Go
    1. Cách viết mã Go
    2. Effective Go
    3. Doc Comment trong Go
  5. Module
    1. Phát triển và publish module
    2. Quản lý phụ thuộc
    3. Tổ chức một module Go
    4. Quản lý mã nguồn của module
    5. Cách đánh số phiên bản module
    6. Quy trình phát hành và đánh phiên bản module
    7. Publish một module
    8. Phát triển một bản cập nhật major version
    9. Tham chiếu tệp go.mod
    10. Ngừng hỗ trợ 'go get' cho việc cài đặt tệp thực thi
  6. Cơ sở dữ liệu (Database)
    1. Truy cập cơ sở dữ liệu quan hệ
    2. Mở một handle cơ sở dữ liệu
    3. Thực thi câu lệnh SQL không trả về dữ liệu
    4. Truy vấn dữ liệu
    5. Dùng prepared statement
    6. Thực thi transaction
    7. Huỷ các thao tác đang chạy
    8. Quản lý kết nối
    9. Tránh rủi ro SQL injection
  7. Chẩn đoán và hiệu năng
    1. Chẩn đoán
    2. Hướng dẫn về Go Garbage Collector
    3. Tối ưu hoá dựa trên profile
    4. Data Race Detector
    5. Gỡ lỗi mã Go bằng GDB
    6. Hỗ trợ coverage profiling cho integration test
  8. Bảo mật (Security)
    1. Tổng quan bảo mật
    2. Thực hành tốt về bảo mật cho lập trình viên Go
    3. Quản lý lỗ hổng trong Go
    4. Cơ sở dữ liệu lỗ hổng của Go
    5. Quét lỗ hổng trong IDE
    6. Chính sách CNA của Go
    7. Go Fuzzing
    8. Chi tiết kỹ thuật của Go Fuzzing
    9. Chính sách bảo mật của Go
    10. Threat Model của Go
    11. Các quyết định về bảo mật của Go
    12. Tuân thủ FIPS 140-3
  9. Công cụ và toolchain
    1. Tài liệu về các lệnh
    2. Về lệnh go
    3. Go Toolchains
    4. Telemetry của Go
    5. Cài đặt Go từ mã nguồn
    6. Cài đặt và sử dụng gccgo
  10. Tham khảo
    1. Câu hỏi thường gặp (FAQ)
    2. Go 1 và tương lai của các chương trình Go
    3. Hướng dẫn chuyển đổi sang encoding/json/v2
    4. Hướng dẫn đóng góp
    5. Đóng góp cho frontend gccgo
  11. Phụ lục
    1. Bảng thuật ngữ
Dạo quanh Go (A Tour of Go)

Các câu lệnh điều khiển luồng: for, if, else, switch và defer

Bản dịch từ go.dev/tour/flowcontrol/1

For

Go chỉ có một cấu trúc lặp duy nhất, đó là vòng lặp for.

Vòng lặp for cơ bản gồm ba thành phần, ngăn cách nhau bằng dấu chấm phẩy:

  • câu lệnh khởi tạo: chạy trước lần lặp đầu tiên
  • biểu thức điều kiện: được tính trước mỗi lần lặp
  • câu lệnh kết thúc vòng: chạy ở cuối mỗi lần lặp

Câu lệnh khởi tạo thường là một khai báo biến, và các biến khai báo ở đó chỉ nhìn thấy được trong phạm vi của câu lệnh for.

Vòng lặp sẽ dừng ngay khi điều kiện kiểu boolean cho ra false.

Lưuý:* Khác với các ngôn ngữ như C, Java hay JavaScript, ba thành phần của câu lệnh for không được bọc trong dấu ngoặc tròn, còn cặp ngoặc nhọn {}` thì luôn bắt buộc.

package main

import "fmt"

func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}

Chạy thử ví dụ này trong Tour of Go ↗

For (tiếp theo)

Câu lệnh khởi tạo và câu lệnh kết thúc vòng là tuỳ chọn.

package main

import "fmt"

func main() {
	sum := 1
	for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
}

Chạy thử ví dụ này trong Tour of Go ↗

For chính là “while” của Go

Đến đây bạn có thể bỏ luôn các dấu chấm phẩy: while của C được viết là for trong Go.

package main

import "fmt"

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}

Chạy thử ví dụ này trong Tour of Go ↗

Lặp mãi mãi

Nếu bạn bỏ điều kiện lặp thì vòng lặp chạy mãi mãi, nên một vòng lặp vô hạn được viết rất gọn.

package main

func main() {
	for {
	}
}

Chạy thử ví dụ này trong Tour of Go ↗

If

Câu lệnh if trong Go giống vòng lặp for của nó; biểu thức không cần được bọc trong dấu ngoặc tròn (), nhưng cặp ngoặc nhọn {} thì bắt buộc.

package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
	if x < 0 {
		return sqrt(-x) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}

Chạy thử ví dụ này trong Tour of Go ↗

If kèm một câu lệnh ngắn

Giống for, câu lệnh if có thể bắt đầu bằng một câu lệnh ngắn chạy trước khi xét điều kiện.

Các biến khai báo trong câu lệnh đó chỉ tồn tại cho tới hết khối if.

(Hãy thử dùng v trong câu lệnh return cuối cùng.)

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

Chạy thử ví dụ này trong Tour of Go ↗

If và else

Các biến khai báo trong câu lệnh ngắn của if cũng dùng được bên trong mọi khối else.

(Cả hai lời gọi pow đều trả về kết quả trước khi lời gọi fmt.Println trong main bắt đầu.)

package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// can't use v here, though
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}

Chạy thử ví dụ này trong Tour of Go ↗

Bài tập: Vòng lặp và hàm

Để nghịch thử với hàm và vòng lặp, hãy cùng hiện thực một hàm căn bậc hai: cho một số x, ta muốn tìm số z sao cho z² gần x nhất.

Máy tính thường tính căn bậc hai của x bằng một vòng lặp. Bắt đầu với một phỏng đoán z nào đó, ta có thể điều chỉnh z dựa trên việc z² gần x tới đâu, để có một phỏng đoán tốt hơn:

z -= (z*z - x) / (2*z)

Lặp lại phép điều chỉnh này khiến phỏng đoán ngày càng tốt hơn, cho tới khi ta đạt tới một kết quả gần với căn bậc hai thật nhất có thể.

Hãy hiện thực điều này trong hàm func Sqrt đã cho sẵn. Một phỏng đoán khởi đầu khá ổn cho z là 1, bất kể giá trị đầu vào là gì. Trước hết, hãy lặp phép tính 10 lần và in ra từng giá trị z trên đường đi. Xem bạn tiến gần tới đáp án đến mức nào với các giá trị x khác nhau (1, 2, 3, …) và phỏng đoán được cải thiện nhanh ra sao.

Gợi ý: Để khai báo và khởi tạo một giá trị dấu phẩy động, hãy viết nó theo cú pháp dấu phẩy động hoặc dùng một phép chuyển kiểu:

z := 1.0
z := float64(1)

Tiếp theo, hãy đổi điều kiện của vòng lặp để nó dừng khi giá trị đã thôi thay đổi (hoặc chỉ thay đổi một lượng rất nhỏ). Xem thử số lần lặp khi đó nhiều hay ít hơn 10. Thử các phỏng đoán khởi đầu khác cho z, ví dụ x, hay x/2. Kết quả của hàm bạn viết gần tới mức nào so với math.Sqrt trong thư viện chuẩn?

(Lưuý:* Nếu bạn quan tâm tới chi tiết của thuật toán, phần z² − x ở trên cho biết z² còn cách nơi nó cần tới (x) bao xa, còn phép chia cho 2z là đạo hàm của z², dùng để chỉnh lượng điều chỉnh z theo tốc độ thay đổi của z². Cách tiếp cận tổng quát này gọi là phương pháp Newton. Nó chạy tốt với nhiều hàm, nhưng đặc biệt tốt với căn bậc hai.)

package main

import (
	"fmt"
)

func Sqrt(x float64) float64 {
}

func main() {
	fmt.Println(Sqrt(2))
}

Chạy thử ví dụ này trong Tour of Go ↗

Switch

Câu lệnh switch (câu lệnh chọn nhánh theo giá trị) là cách viết ngắn gọn cho một chuỗi câu lệnh if-else. Nó chạy case (nhánh) đầu tiên có giá trị bằng với biểu thức điều kiện.

Switch của Go giống như trong C, C++, Java, JavaScript và PHP, chỉ khác là Go chỉ chạy đúng case được chọn, chứ không chạy tiếp mọi case phía sau. Nói cách khác, câu lệnh break mà các ngôn ngữ kia cần đặt ở cuối mỗi case thì Go tự thêm vào sẵn cho bạn. Một khác biệt quan trọng nữa là các case trong switch của Go không nhất thiết phải là hằng số, và các giá trị liên quan cũng không nhất thiết là số nguyên.

package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("macOS.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.\n", os)
	}
}

Chạy thử ví dụ này trong Tour of Go ↗

Thứ tự đánh giá của switch

Switch xét các case từ trên xuống dưới, và dừng lại khi có một case khớp.

(Ví dụ,

switch i {
case 0:
case f():
}

sẽ không gọi f nếu i==0.)

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("When's Saturday?")
	today := time.Now().Weekday()
	switch time.Saturday {
	case today + 0:
		fmt.Println("Today.")
	case today + 1:
		fmt.Println("Tomorrow.")
	case today + 2:
		fmt.Println("In two days.")
	default:
		fmt.Println("Too far away.")
	}
}

Chạy thử ví dụ này trong Tour of Go ↗

Switch không có điều kiện

Switch không kèm điều kiện thì tương đương với switch true.

Cấu trúc này là một cách gọn gàng để viết các chuỗi if-then-else dài.

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}

Chạy thử ví dụ này trong Tour of Go ↗

Defer

Câu lệnh defer (hoãn thực thi) hoãn việc chạy một hàm lại cho tới khi hàm bao quanh nó trả về.

Các đối số của lời gọi được defer sẽ được tính ngay lập tức, nhưng bản thân lời gọi hàm thì không chạy cho tới khi hàm bao quanh trả về.

package main

import "fmt"

func main() {
	defer fmt.Println("world")

	fmt.Println("hello")
}

Chạy thử ví dụ này trong Tour of Go ↗

Xếp chồng nhiều defer

Các lời gọi hàm được defer sẽ được đẩy vào một ngăn xếp. Khi một hàm trả về, các lời gọi được defer của nó chạy theo thứ tự vào sau ra trước.

Để tìm hiểu thêm về câu lệnh defer, hãy đọc bài viết trên blog này.

package main

import "fmt"

func main() {
	fmt.Println("counting")

	for i := 0; i < 10; i++ {
		defer fmt.Println(i)
	}

	fmt.Println("done")
}

Chạy thử ví dụ này trong Tour of Go ↗

Chúc mừng!

Bạn đã hoàn thành bài học này!

Bạn có thể quay lại danh sách các mô-đun để tìm nội dung học tiếp theo, hoặc đi tiếp tới bài học kế tiếp.

Nội dung gốc: Go Documentation © The Go Authors, giấy phép CC BY 3.0. Bản dịch và ghi chú: Lê Văn Dũng.