☰ 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)

Package, biến và hàm

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

Packages

Mọi chương trình Go đều được tạo nên từ các package (đơn vị tổ chức mã nguồn).

Chương trình luôn bắt đầu chạy ở package main.

Chương trình dưới đây dùng hai package có import path (đường dẫn để import) là "fmt" và "math/rand".

Theo quy ước, tên package trùng với phần cuối cùng của import path. Ví dụ, package "math/rand" gồm các tệp bắt đầu bằng câu lệnh package rand.

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println("My favorite number is", rand.Intn(10))
}

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

Imports

Đoạn mã này gom các lệnh import vào chung một cặp ngoặc đơn, gọi là import “gộp” (factored).

Bạn cũng có thể viết nhiều câu lệnh import riêng lẻ, như:

import "fmt"
import "math"

Nhưng cách viết gộp mới là cách viết đẹp, nên dùng.

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Printf("Now you have %g problems.\n", math.Sqrt(7))
}

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

Exported names

Trong Go, một cái tên là exported name (tên viết hoa chữ đầu, package khác dùng được) nếu nó bắt đầu bằng chữ in hoa. Ví dụ, Pizza là một exported name; Pi cũng vậy, nó được export từ package math.

pizza và pi không bắt đầu bằng chữ in hoa, nên chúng không được export.

Khi import một package, bạn chỉ dùng được các exported name của nó. Mọi cái tên “không export” đều không truy cập được từ bên ngoài package.

Hãy chạy thử đoạn mã. Để ý thông báo lỗi.

Để sửa lỗi, đổi math.pi thành math.Pi rồi chạy lại.

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.pi)
}

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

Hàm

Một hàm có thể nhận không hoặc nhiều đối số.

Trong ví dụ này, add nhận hai tham số kiểu int.

Để ý rằng kiểu dữ liệu đứng sau tên biến.

(Muốn biết vì sao cú pháp khai báo lại trông như vậy, xem bài viết về cú pháp khai báo của Go.)

package main

import "fmt"

func add(x int, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}

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

Hàm (tiếp)

Khi hai hay nhiều tham số liền nhau có cùng kiểu, bạn được phép bỏ bớt kiểu ở tất cả, chỉ giữ lại ở tham số cuối.

Trong ví dụ này, chúng tôi rút gọn

x int, y int

thành

x, y int
package main

import "fmt"

func add(x, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}

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

Nhiều giá trị trả về

Một hàm có thể trả về bao nhiêu giá trị cũng được.

Hàm swap ở đây trả về hai chuỗi.

package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}

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

Đặt tên cho giá trị trả về

Giá trị trả về trong Go có thể được đặt tên. Khi đó chúng được xem như các biến khai báo ở đầu hàm.

Nên dùng những cái tên này để nói rõ ý nghĩa của từng giá trị trả về.

Câu lệnh return không kèm gì phía sau sẽ trả về đúng các giá trị đã đặt tên. Kiểu này gọi là “naked” return (return trần).

Chỉ nên dùng “naked” return trong hàm ngắn, như ví dụ ở đây. Trong hàm dài, nó làm mã khó đọc.

package main

import "fmt"

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

func main() {
	fmt.Println(split(17))
}

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

Biến

Câu lệnh var khai báo một danh sách biến; giống như danh sách tham số của hàm, kiểu dữ liệu đứng sau cùng.

Câu lệnh var có thể nằm ở cấp package hoặc cấp hàm. Ví dụ này có cả hai.

package main

import "fmt"

var c, python, java bool

func main() {
	var i int
	fmt.Println(i, c, python, java)
}

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

Biến kèm giá trị khởi tạo

Một khai báo var có thể kèm giá trị khởi tạo, mỗi biến một giá trị.

Nếu đã có giá trị khởi tạo thì được phép bỏ kiểu đi; biến sẽ lấy luôn kiểu của giá trị đó.

package main

import "fmt"

var i, j int = 1, 2

func main() {
	var c, python, java = true, false, "no!"
	fmt.Println(i, j, c, python, java)
}

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

Khai báo biến rút gọn

Bên trong một hàm, bạn có thể dùng câu lệnh gán rút gọn := thay cho khai báo var mà không cần ghi kiểu.

Bên ngoài hàm, mọi câu lệnh đều phải bắt đầu bằng một từ khoá (var, func, v.v.) nên không dùng được := ở đó.

package main

import "fmt"

func main() {
	var i, j int = 1, 2
	k := 3
	c, python, java := true, false, "no!"

	fmt.Println(i, j, k, c, python, java)
}

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

Các kiểu cơ bản

Các kiểu dữ liệu cơ bản của Go là

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128

Ví dụ dưới đây khai báo các biến thuộc nhiều kiểu khác nhau, đồng thời cho thấy khai báo biến cũng có thể gom thành khối giống như các câu lệnh import.

Kiểu int, uint và uintptr thường rộng 32 bit trên hệ thống 32 bit, và 64 bit trên hệ thống 64 bit. Khi cần một giá trị số nguyên, bạn nên dùng int, trừ khi có lý do cụ thể để chọn kiểu số nguyên có kích thước xác định hoặc không dấu.

package main

import (
	"fmt"
	"math/cmplx"
)

var (
	ToBe   bool       = false
	MaxInt uint64     = 1<<64 - 1
	z      complex128 = cmplx.Sqrt(-5 + 12i)
)

func main() {
	fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe)
	fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt)
	fmt.Printf("Type: %T Value: %v\n", z, z)
}

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

Zero value

Biến được khai báo mà không cho giá trị ban đầu sẽ nhận zero_value (giá trị mặc định) của kiểu đó.

Zero value là:

  • 0 với các kiểu số,
  • false với kiểu boolean, và
  • "" (chuỗi rỗng) với chuỗi.
package main

import "fmt"

func main() {
	var i int
	var f float64
	var b bool
	var s string
	fmt.Printf("%v %v %v %q\n", i, f, b, s)
}

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

Type conversion

Biểu thức T(v) chuyển giá trị v sang kiểu T (ép kiểu).

Vài ví dụ ép kiểu số:

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

Hoặc viết gọn hơn:

i := 42
f := float64(i)
u := uint(f)

Khác với C, trong Go việc gán giữa hai giá trị khác kiểu bắt buộc phải ép kiểu tường minh. Thử bỏ lệnh ép kiểu float64 hoặc uint trong ví dụ và xem chuyện gì xảy ra.

package main

import (
	"fmt"
	"math"
)

func main() {
	var x, y int = 3, 4
	var f float64 = math.Sqrt(float64(x*x + y*y))
	var z uint = uint(f)
	fmt.Println(x, y, z)
}

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

Type inference

Khi khai báo một biến mà không ghi rõ kiểu (dùng cú pháp := hoặc cú pháp var=` biểu_thức), kiểu của biến được suy ra từ giá trị ở vế phải. Đó gọi là type inference (suy luận kiểu).

Nếu vế phải đã có kiểu rõ ràng, biến mới sẽ mang đúng kiểu đó:

var i int
j := i // j is an int

Nhưng nếu vế phải là một constant số chưa có kiểu, biến mới có thể là int, float64 hay complex128, tuỳ theo độ chính xác của constant đó:

i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

Thử đổi giá trị khởi tạo của v trong đoạn mã ví dụ và xem kiểu của nó thay đổi thế nào.

package main

import "fmt"

func main() {
	v := 42 // change me!
	fmt.Printf("v is of type %T\n", v)
}

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

Constant

Constant (hằng số) được khai báo giống như biến, nhưng dùng từ khoá const.

Constant có thể là ký tự, chuỗi, boolean hoặc giá trị số.

Không thể khai báo constant bằng cú pháp :=.

package main

import "fmt"

const Pi = 3.14

func main() {
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

	const Truth = true
	fmt.Println("Go rules?", Truth)
}

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

Constant số

Constant số là những giá_trị có độ chính xác rất cao.

Một constant chưa có kiểu sẽ nhận kiểu mà ngữ cảnh dùng nó đòi hỏi.

Thử in thêm needInt(Big) xem sao.

(Kiểu int chứa được tối đa một số nguyên 64 bit, đôi khi còn ít hơn.)

package main

import "fmt"

const (
	// Create a huge number by shifting a 1 bit left 100 places.
	// In other words, the binary number that is 1 followed by 100 zeroes.
	Big = 1 << 100
	// Shift it right again 99 places, so we end up with 1<<1, or 2.
	Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
	return x * 0.1
}

func main() {
	fmt.Println(needInt(Small))
	fmt.Println(needFloat(Small))
	fmt.Println(needFloat(Big))
}

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 module để chọn nội dung học tiếp, hoặc đi thẳng tới bài học tiếp theo.

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.