Cách Viết Mã Go
Giới thiệu
Tài liệu này trình bày quá trình phát triển một package Go đơn giản trong một module và giới thiệu công cụ go, cách chuẩn để tải về, build và cài đặt các module, package và lệnh Go.
Tổ chức mã nguồn
Chương trình Go được tổ chức thành các package. Một package là tập hợp các tệp mã nguồn trong cùng một thư mục, được biên dịch cùng nhau. Các hàm, kiểu dữ liệu, biến và hằng số được khai báo trong một tệp mã nguồn đều hiển thị với tất cả các tệp khác trong cùng package.
Một kho lưu trữ chứa một hoặc nhiều module. Một module là tập hợp
các package Go liên quan được phát hành cùng nhau. Một kho lưu trữ Go thường
chỉ chứa một module, đặt tại thư mục gốc. Một tệp tên
go.mod ở đó khai báo đường dẫn module: tiền tố đường dẫn import
cho tất cả các package trong module. Module bao gồm các package trong
thư mục chứa tệp go.mod cũng như các thư mục con,
cho đến thư mục con tiếp theo có chứa tệp go.mod khác (nếu có).
Lưu ý rằng bạn không cần phải đưa mã lên kho lưu trữ từ xa trước khi có thể build nó. Một module có thể được định nghĩa cục bộ mà không cần thuộc về một kho lưu trữ nào. Tuy nhiên, đây là thói quen tốt để tổ chức mã như thể bạn sẽ xuất bản nó sau này.
Đường dẫn của mỗi module không chỉ đóng vai trò là tiền tố đường dẫn import cho các package của nó,
mà còn cho biết nơi lệnh go cần tìm để tải về module đó.
Ví dụ, để tải về module golang.org/x/tools,
lệnh go sẽ truy vấn kho lưu trữ được chỉ định bởi
https://golang.org/x/tools (được mô tả thêm tại đây).
Một đường dẫn import là một chuỗi dùng để import một package. Đường dẫn import
của một package là đường dẫn module kết hợp với thư mục con của nó trong module.
Ví dụ, module github.com/google/go-cmp chứa một package
trong thư mục cmp/. Đường dẫn import của package đó là
github.com/google/go-cmp/cmp. Các package trong thư viện chuẩn không
có tiền tố đường dẫn module.
Chương trình đầu tiên
Để biên dịch và chạy một chương trình đơn giản, hãy chọn một đường dẫn module (chúng ta sẽ dùng
example/user/hello) và tạo một tệp go.mod khai báo nó:
$ mkdir hello # Hoặc clone nó nếu nó đã tồn tại trong hệ thống quản lý phiên bản. $ cd hello $ go mod init example/user/hello go: creating new go.mod: module example/user/hello $ cat go.mod module example/user/hello go 1.16 $
Câu lệnh đầu tiên trong một tệp mã nguồn Go phải là
package name. Các lệnh thực thi luôn phải dùng
package main.
Tiếp theo, tạo một tệp tên hello.go bên trong thư mục đó với
mã Go sau:
package main
import "fmt"
func main() {
fmt.Println("Hello, world.")
}
Bây giờ bạn có thể build và cài đặt chương trình đó bằng công cụ go:
$ go install example/user/hello $
Lệnh này build lệnh hello, tạo ra một tệp nhị phân thực thi.
Sau đó cài đặt tệp nhị phân đó là $HOME/go/bin/hello (hoặc,
trên Windows, %USERPROFILE%\go\bin\hello.exe).
Thư mục cài đặt được điều khiển bởi các biến môi trường
GOPATH và GOBIN.
Nếu GOBIN được đặt, các tệp nhị phân được cài vào thư mục đó.
Nếu GOPATH được đặt, các tệp nhị phân được cài vào
thư mục con bin của thư mục đầu tiên trong danh sách GOPATH.
Nếu không, các tệp nhị phân được cài vào
thư mục con bin của GOPATH mặc định
($HOME/go hoặc %USERPROFILE%\go).
Bạn có thể dùng lệnh go env để đặt giá trị mặc định một cách linh hoạt
cho một biến môi trường, áp dụng cho các lệnh go về sau:
$ go env -w GOBIN=/somewhere/else/bin $
Để hủy đặt một biến đã được đặt trước đó bằng go env -w, dùng go env -u:
$ go env -u GOBIN $
Các lệnh như go install hoạt động trong phạm vi module
chứa thư mục làm việc hiện tại. Nếu thư mục làm việc không nằm trong
module example/user/hello, go install có thể thất bại.
Để thuận tiện, các lệnh go chấp nhận đường dẫn tương đối
so với thư mục làm việc và mặc định dùng package trong
thư mục làm việc hiện tại nếu không có đường dẫn nào khác được cung cấp.
Vì vậy, trong thư mục làm việc của chúng ta, các lệnh sau đây đều tương đương:
$ go install example/user/hello
$ go install .
$ go install
Tiếp theo, hãy chạy chương trình để đảm bảo nó hoạt động. Để thuận tiện hơn, ta sẽ
thêm thư mục cài đặt vào PATH để chạy các tệp nhị phân dễ dàng:
# Người dùng Windows nên tham khảo /wiki/SettingGOPATH
# để đặt %PATH%.
$ export PATH=$PATH:$(dirname $(go list -f '{{.Target}}' .))
$ hello
Hello, world.
$
Nếu bạn đang dùng hệ thống quản lý mã nguồn, đây là thời điểm tốt để khởi tạo một kho lưu trữ, thêm các tệp và commit thay đổi đầu tiên. Lại nữa, bước này là tùy chọn: bạn không cần dùng hệ thống quản lý mã nguồn để viết mã Go.
$ git init Initialized empty Git repository in /home/user/hello/.git/ $ git add go.mod hello.go $ git commit -m "initial commit" [master (root-commit) 0b4507d] initial commit 1 file changed, 7 insertion(+) create mode 100644 go.mod hello.go $
Lệnh go tìm kho lưu trữ chứa một đường dẫn module nhất định bằng cách gửi yêu cầu đến URL HTTPS tương ứng và đọc metadata nhúng trong phản hồi HTML (xem
go help importpath).
Nhiều dịch vụ lưu trữ đã cung cấp sẵn metadata đó cho các kho lưu trữ chứa
mã Go, vì vậy cách dễ nhất để làm cho module của bạn khả dụng cho người khác thường là
đặt đường dẫn module trùng với URL của kho lưu trữ.
Import package từ module của bạn
Hãy viết một package morestrings và dùng nó từ chương trình hello.
Đầu tiên, tạo một thư mục cho package có tên
$HOME/hello/morestrings, sau đó tạo một tệp tên
reverse.go trong thư mục đó với nội dung sau:
// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings
// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
Vì hàm ReverseRunes của chúng ta bắt đầu bằng chữ hoa,
nó được xuất ra ngoài
và có thể được dùng trong các package khác import package morestrings của ta.
Hãy kiểm tra xem package biên dịch được không với go build:
$ cd $HOME/hello/morestrings $ go build $
Lệnh này sẽ không tạo ra tệp đầu ra. Thay vào đó, nó lưu package đã biên dịch vào bộ nhớ đệm build cục bộ.
Sau khi xác nhận package morestrings build thành công, hãy dùng nó
từ chương trình hello. Để làm vậy, chỉnh sửa tệp
$HOME/hello/hello.go gốc để dùng package morestrings:
package main
import (
"fmt"
"example/user/hello/morestrings"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}
Cài đặt chương trình hello:
$ go install example/user/hello
Chạy phiên bản mới của chương trình, bạn sẽ thấy một thông điệp mới, đảo ngược:
$ hello Hello, Go!
Import package từ module từ xa
Một đường dẫn import có thể mô tả cách lấy mã nguồn của package bằng cách dùng
hệ thống quản lý phiên bản như Git hoặc Mercurial. Công cụ go dùng
thuộc tính này để tự động tải về các package từ kho lưu trữ từ xa.
Ví dụ, để dùng github.com/google/go-cmp/cmp trong chương trình:
package main
import (
"fmt"
"example/user/hello/morestrings"
"github.com/google/go-cmp/cmp"
)
func main() {
fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}
Bây giờ bạn có dependency vào một module bên ngoài, bạn cần tải về module đó
và ghi lại phiên bản của nó vào tệp go.mod. Lệnh go
mod tidy thêm các yêu cầu module còn thiếu cho các package được import
và xóa các yêu cầu đối với module không còn được dùng nữa.
$ go mod tidy go: finding module for package github.com/google/go-cmp/cmp go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.4 $ go install example/user/hello $ hello Hello, Go! string( - "Hello World", + "Hello Go", ) $ cat go.mod module example/user/hello go 1.16 require github.com/google/go-cmp v0.5.4 $
Các dependency của module được tải về tự động vào thư mục con pkg/mod
của thư mục được chỉ định bởi biến môi trường GOPATH.
Nội dung đã tải về cho một phiên bản cụ thể của module được chia sẻ
giữa tất cả các module khác require phiên bản đó, vì vậy
lệnh go đánh dấu các tệp và thư mục đó là chỉ đọc. Để
xóa tất cả module đã tải về, bạn có thể truyền cờ -modcache
cho go clean:
$ go clean -modcache $
Kiểm thử
Go có framework kiểm thử nhẹ gồm lệnh go test
và package testing.
Bạn viết kiểm thử bằng cách tạo một tệp có tên kết thúc bằng _test.go
chứa các hàm tên TestXXX với chữ ký
func (t *testing.T).
Framework kiểm thử chạy từng hàm đó;
nếu hàm gọi một hàm báo thất bại như t.Error hoặc
t.Fail, kiểm thử được coi là đã thất bại.
Thêm kiểm thử vào package morestrings bằng cách tạo tệp
$HOME/hello/morestrings/reverse_test.go chứa
mã Go sau.
package morestrings
import "testing"
func TestReverseRunes(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := ReverseRunes(c.in)
if got != c.want {
t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
}
}
}
Sau đó chạy kiểm thử với go test:
$ cd $HOME/hello/morestrings $ go test PASS ok example/user/hello/morestrings 0.165s $
Chạy go help test và xem
tài liệu package testing để biết thêm chi tiết.
Các bước tiếp theo
Đăng ký danh sách thư golang-announce để được thông báo khi có phiên bản ổn định mới của Go được phát hành.
Xem Effective Go để có các mẹo viết mã Go rõ ràng, tự nhiên.
Tham gia A Tour of Go để học ngôn ngữ một cách bài bản.
Truy cập trang tài liệu để xem tập hợp các bài viết chuyên sâu về ngôn ngữ Go, thư viện và công cụ của nó.
Nhận trợ giúp
Để được hỗ trợ trực tiếp, hãy hỏi những gopher nhiệt tình trên máy chủ Slack gophers do cộng đồng vận hành (lấy lời mời tại đây).
Danh sách thư chính thức để thảo luận về ngôn ngữ Go là Go Nuts.
Báo cáo lỗi bằng trình theo dõi issue của Go.