Go速查手册
Go语法/goroutine/常用包速查表
📦 模块与 go mod
新建工程第一步,module 路径通常是仓库地址。
go mod init example.com/hello
# 生成 go.mod,之后构建会自动补全 go.sum
go mod tidy # 增删依赖,整理 go.mod
go mod vendor # 依赖拷贝到 vendor 目录
go mod graph # 查看依赖图
同目录文件 package 名相同;main 包才是可执行程序。
package main
import (
"fmt"
"strings"
"github.com/gin-gonic/gin" // 第三方包
)
func main() {
fmt.Println(strings.ToUpper("go"))
_ = gin.New()
}
go get 指定版本/分支/提交;go mod tidy 同步。
go get github.com/gin-gonic/gin
go get github.com/gin-gonic/gin@v1.9.1
go get github.com/gin-gonic/gin@latest
go get github.com/gin-gonic/gin@main # 分支
go get -u ./... # 升级全部直接/间接依赖
go mod edit -droprequire=github.com/x/y # 移除依赖记录
run 临时编译运行,build 产出二进制;GOOS/GOARCH 交叉编译。
go run .
go run main.go util.go
go build -o app .
go install ./... # 安装到 $GOBIN
# 交叉编译 Linux 可执行文件(在 Windows/macOS 上)
GOOS=linux GOARCH=amd64 go build -o app-linux .
# Windows 上:set GOOS=linux&& set GOARCH=amd64&& go build .
CGO_ENABLED=0 go build -ldflags="-s -w" .
📝 基本语法
函数内首选 := 短声明;未使用的变量会编译报错。
var a int = 1
var b, c = 2, "hi"
d := 3.14
var (
name = "Tom"
age = 18
)
const Pi = 3.14159
const (
StatusOK = 200
StatusNotFound = 404
)
声明即初始化为零值:数字 0、字符串 ""、bool false、指针/切片/map 为 nil。
var (
i int // 平台位宽,另有 int8/16/32/64、uint 系列
f32 float32
f64 float64
b bool
s string
by byte // = uint8
r rune // = int32,表示一个 Unicode 码点
)
// 类型转换必须显式
n := int(f64)
str := string(r)
条件无需小括号;可带初始化语句,err 只在 if 块内有效。
if age >= 18 {
fmt.Println("成年")
} else if age >= 12 {
fmt.Println("少年")
} else {
fmt.Println("儿童")
}
// 常用:先取错误再判断
if err := doSomething(); err != nil {
return err
}
Go 只有 for,没有 while;range 遍历切片/map/字符串/通道。
for i := 0; i < 5; i++ {
fmt.Println(i)
}
for x < 100 { // 等价 while
x *= 2
}
nums := []string{"a", "b", "c"}
for i, v := range nums {
fmt.Println(i, v)
}
for _, v := range nums { } // 只要值
for i := range nums { } // 只要索引
默认不穿透(无需 break),需要继续用 fallthrough;还可不带表达式。
switch os := runtime.GOOS; os {
case "linux":
fmt.Println("Linux")
case "darwin":
fmt.Println("macOS")
default:
fmt.Println("其他")
}
switch { // 无表达式,等价 if-else 链
case n < 0:
fmt.Println("负")
case n == 0:
fmt.Println("零")
}
同类型参数合并声明;命名返回值可配合裸 return。
func div(a, b int) (int, int) {
return a / b, a % b
}
q, r := div(10, 3)
// 命名返回值
func minMax(vs []int) (mn, mx int) {
mn, mx = vs[0], vs[0]
for _, v := range vs {
if v < mn { mn = v }
if v > mx { mx = v }
}
return
}
& 取地址,* 解引用;函数内修改外部变量需传指针。
x := 10
p := &x // *int,指向 x
fmt.Println(*p) // 10
*p = 20
fmt.Println(x) // 20
func inc(n *int) { *n++ }
inc(&x)
函数返回前按 LIFO 顺序执行,常用于关闭资源。
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 函数退出时关闭
// ...
return nil
}
defer fmt.Println("first")
defer fmt.Println("second")
// 输出顺序:second -> first
🧱 切片 / Map / 字符串
引用类型,三要素:指针、长度 len、容量 cap。
s := []int{1, 2, 3}
s = append(s, 4)
s = append(s, 5, 6, 7)
s = append(s, other...) // 追加另一个切片
s2 := make([]int, 0, 10) // len=0 cap=10
n := copy(dst, src) // 返回拷贝元素数
sub := s[1:3] // 切片共享底层数组
s = append(s[:1], s[2:]...) // 删除下标 1 的元素
键存在性用“逗号 ok”写法判断;并发读写需加锁或 sync.Map。
m := map[string]int{"a": 1}
m["b"] = 2
v, ok := m["x"] // 不存在时 v 是零值,ok=false
if age, ok := m["Tom"]; ok {
fmt.Println(age)
}
delete(m, "a")
mm := make(map[string][]string) // 值为切片
mm["k"] = append(mm["k"], "v1")
Go 字符串是只读字节序列,字符串处理全在 strings 包。
strings.Contains("hello", "ell") // true
strings.HasPrefix("main.go", "main")
strings.HasSuffix("main.go", ".go")
strings.Join([]string{"a", "b"}, "-") // "a-b"
strings.Split("a,b,c", ",") // [a b c]
strings.Replace("aaa", "a", "b", 2) // "bba"
strings.ReplaceAll("aaa", "a", "b")
strings.TrimSpace(" hi ")
strings.ToLower/ToUpper("AbC")
strings.Repeat("ab", 3) // "ababab"
Sprintf 也能转,但 strconv 更快更明确。
n, err := strconv.Atoi("123") // 字符串 -> int
s := strconv.Itoa(123) // int -> "123"
f, err := strconv.ParseFloat("3.14", 64)
x, err := strconv.ParseInt("ff", 16, 64) // 按进制
b, err := strconv.ParseBool("true")
fmt.Sprintf("%d", n) // 万能但较慢
🏛 struct 与接口
结构体标签(反引号内容)常用于 JSON 序列化字段映射。
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age,omitempty"`
}
// 值接收者:不改副本以外的状态
func (u User) Greet() string {
return "hi " + u.Name
}
// 指针接收者:可修改原对象
func (u *User) Birthday() {
u.Age++
}
Go 没有构造函数语法,约定用 NewXxx 函数返回指针。
func NewUser(name string, age int) *User {
return &User{
ID: 1,
Name: name,
Age: age,
}
}
u := NewUser("Tom", 18)
fmt.Printf("%+v\n", u) // 带字段名打印
不需要 implements 关键字,实现全部方法即自动满足接口。
type Stringer interface {
String() string
}
type Speaker interface {
Stringer // 嵌入接口
Speak() string
}
// 面向接口编程
func Print(s Stringer) {
fmt.Println(s.String())
}
var _ Stringer = (*User)(nil) // 编译期断言 User 实现接口
从接口值取出具体类型;any = interface{}。
var v any = "hello"
s, ok := v.(string) // 安全断言
fmt.Println(s, ok)
switch val := v.(type) {
case string:
fmt.Println("字符串", val)
case int:
fmt.Println("整数", val)
case nil:
fmt.Println("nil")
default:
fmt.Printf("其他 %T\n", val)
}
🌀 goroutine 与 channel
go 关键字即可开启轻量协程;闭包捕获循环变量要注意传参。
go doWork()
go func(msg string) {
fmt.Println(msg)
}("hello")
// 循环中启动(Go 1.22 前需显式传参)
for i := 0; i < 3; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}
无缓冲通道发送/接收同步配对;关闭后 range 自动结束。
ch := make(chan int) // 无缓冲
buf := make(chan string, 2) // 缓冲为 2
ch <- 1 // 发送
v := <-ch // 接收
close(ch) // 关闭(由发送方关闭)
for v := range ch { // 通道关闭后循环退出
fmt.Println(v)
}
v, ok := <-ch // ok=false 表示通道已关闭且取空
同时等待多个通道;常配 time.After 做超时控制。
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
case <-time.After(3 * time.Second):
fmt.Println("超时")
default:
fmt.Println("都没就绪,非阻塞")
}
等待一组 goroutine 结束;共享数据加锁避免竞态。
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println("task", n)
}(i)
}
wg.Wait()
var mu sync.Mutex
count := 0
mu.Lock()
count++
mu.Unlock()
// RWMutex:读多写少场景用 RLock/RUnlock
⚠️ 错误处理
Go 把错误作为普通返回值;%w 包装保留错误链。
n, err := strconv.Atoi(s)
if err != nil {
return fmt.Errorf("解析 %q 失败: %w", s, err)
}
// 创建错误
var ErrNotFound = errors.New("not found")
err := fmt.Errorf("用户 %d 不存在: %w", id, ErrNotFound)
被 %w 包装后也能沿错误链判断/提取。
if errors.Is(err, os.ErrNotExist) {
fmt.Println("文件不存在")
}
if errors.Is(err, context.Canceled) {
return
}
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("路径错误:", pathErr.Path)
}
业务错误不要用 panic;recover 只能在 defer 函数中生效。
func safe() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recover: %v", r)
}
}()
panic("出大事了")
}
// 主动 panic:必须真正不可恢复时才用
panic(fmt.Sprintf("非法配置: %s", conf))
📚 常用标准库
%d 整数、%s 字符串、%v 通用、%+v 带字段名、%T 类型。
fmt.Printf("%d-%s-%.2f\n", 1, "x", 3.1415)
s := fmt.Sprintf("%05d", 42) // "00042"
fmt.Println("a", 1, true) // 空格连接并换行
fmt.Printf("%v %+v %#v\n", u, u, u)
fmt.Printf("%T\n", u) // 打印类型
fmt.Fprintf(w, "ok") // 写到 io.Writer
小文件用 ReadFile/WriteFile 一步到位。
data, err := os.ReadFile("a.txt")
err = os.WriteFile("b.txt", []byte("hi"), 0644)
os.Getenv("HOME")
os.Setenv("APP_ENV", "dev")
os.Args // 命令行参数
os.MkdirAll("a/b/c", 0755)
os.Remove("f.txt")
os.Rename("old", "new")
字段需大写可导出;标签 json:"-" 可跳过字段。
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Pwd string `json:"-"`
}
u := User{Name: "Tom", Age: 18, Pwd: "x"}
b, _ := json.Marshal(u) // -> []byte
b, _ = json.MarshalIndent(u, "", " ") // 缩进
var v User
json.Unmarshal([]byte(`{"name":"Amy","age":20}`), &v)
var anyV map[string]any // 结构未知时
json.Unmarshal(data, &anyV)
Go 参考时间是 2006-01-02 15:04:05(记忆口诀)。
now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05"))
t, err := time.Parse("2006-01-02", "2026-10-04")
t2, _ := time.Parse(time.RFC3339, "2026-10-04T08:00:00Z")
now.Add(24 * time.Hour)
now.AddDate(0, 1, 0) // 加 1 个月
time.Sleep(500 * time.Millisecond)
time.After(3 * time.Second) // 返回 <-chan time.Time
time.Since(t) // 耗时
🌐 net/http
注册处理函数即可启动服务;生产建议用 &http.Server 配置超时。
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, `{"ok":true}`)
})
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Fprintf(w, "hello %s", name)
})
log.Println("listen :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
io.ReadAll 读原始请求体;json.NewDecoder 直接解码。
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(u)
})
始终关闭 Body;自定义 Client 设置超时。
resp, err := http.Get("https://api.example.com/v1/ping")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(body))
client := &http.Client{Timeout: 5 * time.Second}
req, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(`{"a":1}`))
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
✅ 测试
xxx_test.go 中 TestXxx(t *testing.T) 即被 go test 识别。
// calc.go
func Add(a, b int) int { return a + b }
// calc_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
if got := Add(1, 2); got != 3 {
t.Fatalf("Add(1,2) = %d, want 3", got)
}
}
匿名结构体切片集中组织用例,是 Go 社区主流写法。
func TestAddTable(t *testing.T) {
cases := []struct {
name string
a, b, want int
}{
{"正数", 1, 2, 3},
{"负数", -1, -2, -3},
{"零", 0, 0, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if Add(c.a, c.b) != c.want {
t.Fatalf("got %d want %d", Add(c.a, c.b), c.want)
}
})
}
}
-run 过滤用例、-v 详细输出、-cover 覆盖率、-bench 基准。
go test ./...
go test -v -run TestAdd ./...
go test -run TestAddTable/正数 ./...
go test -cover ./...
go test -coverprofile=cover.out ./...
go tool cover -html=cover.out
go test -bench=. -benchmem ./...
go test -race ./... # 竞态检测
😶 没有匹配的条目,换个关键词试试
📖 使用说明
全程在浏览器本地运行。
操作步骤:
- 搜索或浏览分类条目;
- 查看语法与示例代码;
- 点击复制代码块。
💬 用户评论 (0)
还没有评论,快来抢沙发!