首页 / 速查手册 / 在线

Go速查手册

Go语法/goroutine/常用包速查表

速查手册 · 1 次 · 2026-10-04 · 分享 · 全屏

📦 模块与 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)
if 条件

条件无需小括号;可带初始化语句,err 只在 if 块内有效。

if age >= 18 {
    fmt.Println("成年")
} else if age >= 12 {
    fmt.Println("少年")
} else {
    fmt.Println("儿童")
}

// 常用:先取错误再判断
if err := doSomething(); err != nil {
    return err
}
for 三种形式

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 { }      // 只要索引
switch

默认不穿透(无需 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)
defer 延迟执行

函数返回前按 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 / 字符串

切片 slice

引用类型,三要素:指针、长度 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 的元素
映射 map

键存在性用“逗号 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")
strings 包

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"
strconv 数字字符串转换

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 实现接口
类型断言与 type switch

从接口值取出具体类型;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

启动 goroutine

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)
}
channel 通道

无缓冲通道发送/接收同步配对;关闭后 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 表示通道已关闭且取空
select 多路复用

同时等待多个通道;常配 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("都没就绪,非阻塞")
}
WaitGroup 与 Mutex

等待一组 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

⚠️ 错误处理

error 与包装

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)
errors.Is / errors.As

被 %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

业务错误不要用 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))

📚 常用标准库

fmt 格式化

%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
os 读写文件与环境

小文件用 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")
encoding/json

字段需大写可导出;标签 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)
time 时间处理

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 服务

注册处理函数即可启动服务;生产建议用 &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))
}
读取 POST Body 与 JSON

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)
})
HTTP 客户端

始终关闭 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)
            }
        })
    }
}
常用 go test 命令

-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 ./...           # 竞态检测

📖 使用说明

全程在浏览器本地运行。

操作步骤:

  1. 搜索或浏览分类条目;
  2. 查看语法与示例代码;
  3. 点击复制代码块。

💬 用户评论 (0)

还没有评论,快来抢沙发!

请添加微信联系我