Blog Home
Updated: 2025 Nov 04

Go 语言完整教程

前言

Go 语言(又称 Golang)是由 Google 开发的开源编程语言,于 2009 年发布。它结合了静态类型语言的安全性和动态语言的开发效率。

为什么学习 Go?

  • 简洁明了: 语法简单,易于学习
  • 高性能: 编译型语言,接近 C 的执行速度
  • 并发支持: 内置 goroutine 和 channel
  • 快速编译: 编译速度极快
  • 强大工具链: 自带格式化、测试、文档工具
  • 广泛应用: Docker、Kubernetes、Prometheus 等知名项目

Go 语言设计理念

核心设计原则

1. 简洁性 (Simplicity)

Go 刻意保持语言特性的最小化,没有继承、泛型(Go 1.18 后添加)、异常等复杂特性。

"Simplicity is complicated." - Rob Pike

2. 明确性 (Explicitness)

Go 强调明确而非隐式:

  • 没有隐式类型转换
  • 错误必须显式处理
  • 导入的包必须使用

3. 组合优于继承 (Composition over Inheritance)

Go 使用接口和嵌入实现代码复用,而不是传统的继承。

4. 并发即语言特性 (Concurrency as Language Feature)

通过 goroutine 和 channel 使并发编程变得简单自然。

5. 工程化思维 (Engineering Focus)

Go 为大型团队协作设计:

  • 强制代码格式化(gofmt)
  • 快速编译
  • 严格的依赖管理
  • 内置测试框架

Go 的设计取舍

特性 Go 的选择 原因
继承 ❌ 不支持 避免复杂的类层次结构
泛型 ✅ Go 1.18+ 社区强烈需求,谨慎添加
异常 ❌ 使用错误值 明确错误处理路径
运算符重载 ❌ 不支持 保持代码可读性
默认参数 ❌ 不支持 避免函数调用歧义
GC ✅ 自动垃圾回收 简化内存管理

环境搭建

安装 Go

Linux/macOS

# 下载(以 1.21.0 为例)
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz

# 解压
sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz

# 添加到 PATH
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc

Windows

下载 MSI 安装包: https://go.dev/dl/

验证安装

go version
# 输出: go version go1.21.0 linux/amd64

配置开发环境

设置 GOPATH(可选,使用 Go Modules 后不必需)

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

启用 Go Modules

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct  # 中国用户加速

推荐编辑器

  • VSCode + Go 插件
  • GoLand (JetBrains)
  • Vim/Neovim + vim-go
  • Emacs + go-mode

第一个程序

// hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界!")
}
# 运行
go run hello.go

# 编译
go build hello.go
./hello

# 格式化代码
gofmt -w hello.go

基础语法

包声明

每个 Go 文件必须以包声明开始。可执行程序必须包含 main 包和 main 函数。

package main  // 可执行程序

// 或者
package mylib  // 库包

导入

单个导入

import "fmt"
import "math"

多个导入(推荐)

import (
    "fmt"
    "math"
    "strings"
)

导入别名

import (
    f "fmt"           // 别名
    . "math"          // 点导入,可直接使用 Sin 而非 math.Sin
    _ "image/png"     // 仅执行包的 init 函数
)

变量声明

完整声明

var name string = "张三"
var age int = 25
var isStudent bool = true

类型推断

var name = "张三"     // string
var age = 25         // int
var pi = 3.14        // float64

短变量声明(最常用)

name := "张三"
age := 25
x, y := 100, 200     // 多重赋值

注意: 短变量声明只能在函数内使用。

多变量声明

var (
    name   string = "Alice"
    age    int    = 30
    salary float64
)

零值

未初始化的变量有零值:

  • 数值类型: 0
  • 布尔类型: false
  • 字符串: ""
  • 指针、切片、映射、通道、接口: nil
var count int        // 0
var name string      // ""
var ptr *int         // nil

常量

定义常量

const Pi = 3.14159
const MaxSize = 100

const (
    StatusOK = 200
    StatusNotFound = 404
    StatusError = 500
)

iota 枚举生成器

const (
    Sunday = iota    // 0
    Monday           // 1
    Tuesday          // 2
    Wednesday        // 3
    Thursday         // 4
    Friday           // 5
    Saturday         // 6
)

// 位运算示例
const (
    Read = 1 << iota   // 1 << 0 = 1
    Write              // 1 << 1 = 2
    Execute            // 1 << 2 = 4
)

// 跳过值
const (
    _ = iota        // 0,跳过
    KB = 1 << (10 * iota)  // 1 << 10 = 1024
    MB              // 1 << 20 = 1048576
    GB              // 1 << 30
    TB              // 1 << 40
)

注释

// 单行注释

/*
多行注释
可以跨越多行
*/

// 文档注释(包、函数、类型前)
// 会被 godoc 工具提取

// Add 计算两个整数的和
// 参数 a, b 为加数
// 返回值为和
func Add(a, b int) int {
    return a + b
}

数据类型

基本类型

布尔类型

var flag bool = true
var isReady bool = false

// 布尔运算
result := true && false  // false
result = true || false   // true
result = !true           // false

整数类型

类型 大小 范围
int8 8位 -128 到 127
int16 16位 -32768 到 32767
int32 32位 -231 到 231-1
int64 64位 -263 到 263-1
uint8 8位 0 到 255
uint16 16位 0 到 65535
uint32 32位 0 到 232-1
uint64 64位 0 到 264-1
int 平台相关 32 或 64 位
uint 平台相关 32 或 64 位
uintptr 平台相关 存储指针
var age int = 25
var count uint = 100
var small int8 = 127
var big int64 = 9223372036854775807

// 类型转换(必须显式)
var x int = 10
var y float64 = float64(x)
var z uint = uint(x)

浮点类型

var pi float32 = 3.14159
var e float64 = 2.718281828459045

// 科学计数法
var large = 1.2e9   // 1.2 * 10^9
var small = 3.5e-5  // 3.5 * 10^-5

// 特殊值
import "math"
var inf = math.Inf(1)    // 正无穷
var nan = math.NaN()     // 非数字

复数类型

var c1 complex64 = 1 + 2i
var c2 complex128 = complex(3, 4)  // 3 + 4i

// 操作
real := real(c2)  // 3
imag := imag(c2)  // 4

字符串类型

var s1 string = "Hello, 世界"
var s2 = "Go 语言"

// 原始字符串(不转义)
var path = `C:\Users\name\file.txt`
var multiline = `
这是
多行
字符串
`

// 字符串操作
length := len(s1)           // 字节长度
char := s1[0]               // 字节访问
substr := s1[0:5]           // 切片
concat := s1 + " " + s2     // 拼接

// 字符串是不可变的
// s1[0] = 'h'  // 错误!

// 遍历
for i, ch := range "Go语言" {
    fmt.Printf("%d: %c\n", i, ch)
}

byte 和 rune

// byte = uint8, 表示 ASCII 字符
var b byte = 'A'

// rune = int32, 表示 Unicode 码点
var r rune = '中'

// 字符串转换
s := "Hello"
bytes := []byte(s)      // 转为字节切片
runes := []rune(s)      // 转为 rune 切片

// 中文处理
chinese := "你好世界"
fmt.Println(len(chinese))           // 12 (字节数)
fmt.Println(len([]rune(chinese)))   // 4 (字符数)

指针

Go 有指针,但没有指针运算(更安全)。

var x int = 100
var p *int = &x      // p 是指向 x 的指针

fmt.Println(p)       // 0xc000012088 (地址)
fmt.Println(*p)      // 100 (解引用)

*p = 200             // 通过指针修改值
fmt.Println(x)       // 200

// 零值
var ptr *int         // nil
// fmt.Println(*ptr) // panic: 空指针解引用

// new 函数分配内存
ptr = new(int)       // 分配 int 并返回指针
*ptr = 42

指针的用途

// 1. 函数参数传递(避免拷贝大型结构)
func updateValue(p *int) {
    *p = 999
}

// 2. 共享数据
type Data struct {
    Value int
}

func modifyData(d *Data) {
    d.Value = 100  // 自动解引用,等价于 (*d).Value
}

func main() {
    num := 10
    updateValue(&num)
    fmt.Println(num)  // 999

    data := &Data{Value: 1}
    modifyData(data)
    fmt.Println(data.Value)  // 100
}

控制结构

if 语句

基本形式

age := 18

if age >= 18 {
    fmt.Println("成年人")
}

if age >= 18 {
    fmt.Println("成年人")
} else {
    fmt.Println("未成年")
}

if age < 13 {
    fmt.Println("儿童")
} else if age < 18 {
    fmt.Println("青少年")
} else {
    fmt.Println("成年人")
}

if 简短语句

// 在 if 中声明变量(作用域仅在 if-else 块内)
if score := getScore(); score >= 90 {
    fmt.Println("优秀")
} else if score >= 60 {
    fmt.Println("及格")
} else {
    fmt.Println("不及格")
}
// score 在此处不可见

for 循环

Go 只有一种循环结构: for

经典三段式

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

while 风格

count := 0
for count < 5 {
    fmt.Println(count)
    count++
}

无限循环

for {
    // 无限循环
    // 使用 break 退出
    if condition {
        break
    }
}

range 遍历

// 遍历切片
nums := []int{1, 2, 3, 4, 5}
for index, value := range nums {
    fmt.Printf("nums[%d] = %d\n", index, value)
}

// 只要索引
for i := range nums {
    fmt.Println(i)
}

// 只要值(使用 _ 忽略索引)
for _, v := range nums {
    fmt.Println(v)
}

// 遍历字符串(按 rune)
for i, ch := range "Go语言" {
    fmt.Printf("%d: %c\n", i, ch)
}

// 遍历 map
ages := map[string]int{"Alice": 25, "Bob": 30}
for name, age := range ages {
    fmt.Printf("%s: %d\n", name, age)
}

// 遍历 channel
ch := make(chan int)
go func() {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}()

for value := range ch {
    fmt.Println(value)
}

break 和 continue

// break 退出循环
for i := 0; i < 10; i++ {
    if i == 5 {
        break  // 停止循环
    }
    fmt.Println(i)
}

// continue 跳过本次迭代
for i := 0; i < 10; i++ {
    if i%2 == 0 {
        continue  // 跳过偶数
    }
    fmt.Println(i)  // 只打印奇数
}

// 标签(用于多层循环)
outer:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if i*j > 2 {
            break outer  // 跳出外层循环
        }
        fmt.Printf("(%d,%d) ", i, j)
    }
}

switch 语句

基本形式

day := "Monday"

switch day {
case "Monday":
    fmt.Println("星期一")
case "Tuesday":
    fmt.Println("星期二")
case "Wednesday", "Thursday", "Friday":
    fmt.Println("工作日")
case "Saturday", "Sunday":
    fmt.Println("周末")
default:
    fmt.Println("未知")
}

注意: Go 的 switch 自动 break,不需要显式写。

switch 简短语句

switch score := getScore(); {
case score >= 90:
    fmt.Println("A")
case score >= 80:
    fmt.Println("B")
case score >= 70:
    fmt.Println("C")
case score >= 60:
    fmt.Println("D")
default:
    fmt.Println("F")
}

无条件 switch(等价于 if-else 链)

age := 25

switch {
case age < 13:
    fmt.Println("儿童")
case age < 18:
    fmt.Println("青少年")
case age < 60:
    fmt.Println("成年人")
default:
    fmt.Println("老年人")
}

类型 switch

func checkType(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("整数: %d\n", v)
    case string:
        fmt.Printf("字符串: %s\n", v)
    case bool:
        fmt.Printf("布尔: %t\n", v)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
}

checkType(42)        // 整数: 42
checkType("hello")   // 字符串: hello
checkType(true)      // 布尔: true

fallthrough

// fallthrough 强制执行下一个 case
switch num := 2; num {
case 1:
    fmt.Println("一")
case 2:
    fmt.Println("二")
    fallthrough  // 继续执行下一个 case
case 3:
    fmt.Println("三")
}
// 输出: 二 三

defer 延迟执行

defer 用于延迟函数调用,直到包含它的函数返回。

func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        return
    }
    defer file.Close()  // 函数返回时自动关闭文件

    // 读取文件...
}

// 多个 defer 按 LIFO(后进先出)顺序执行
func example() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    fmt.Println("函数体")
}
// 输出: 函数体 3 2 1

defer 的常见用途

// 1. 资源释放
func processFile(filename string) error {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer f.Close()

    // 处理文件
    return nil
}

// 2. 互斥锁解锁
var mu sync.Mutex

func criticalSection() {
    mu.Lock()
    defer mu.Unlock()  // 确保解锁

    // 临界区代码
}

// 3. 错误恢复
func safeFunction() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("捕获 panic:", r)
        }
    }()

    // 可能 panic 的代码
}

// 4. 性能测量
func measureTime(name string) func() {
    start := time.Now()
    return func() {
        fmt.Printf("%s 耗时: %v\n", name, time.Since(start))
    }
}

func slowFunction() {
    defer measureTime("slowFunction")()

    // 耗时操作
    time.Sleep(2 * time.Second)
}

goto 和标签

Go 支持 goto,但应谨慎使用。

func checkError() {
    err := doSomething()
    if err != nil {
        goto Error
    }

    err = doAnotherThing()
    if err != nil {
        goto Error
    }

    return

Error:
    fmt.Println("发生错误:", err)
    cleanup()
}

函数

函数是 Go 的一等公民,是组织代码的基本单元。

函数定义

// 基本形式
func functionName(param1 type1, param2 type2) returnType {
    // 函数体
    return value
}

// 示例
func add(a int, b int) int {
    return a + b
}

// 相同类型参数简写
func multiply(a, b, c int) int {
    return a * b * c
}

// 无参数无返回值
func sayHello() {
    fmt.Println("Hello!")
}

// 无返回值
func printSum(a, b int) {
    fmt.Println(a + b)
}

多返回值

// 多个返回值
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为0")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("错误:", err)
        return
    }
    fmt.Println("结果:", result)
}

// 忽略返回值
result, _ := divide(10, 2)  // 忽略错误

命名返回值

// 命名返回值相当于在函数开头声明变量
func divide(a, b float64) (result float64, err error) {
    if b == 0 {
        err = fmt.Errorf("除数不能为0")
        return  // 裸返回,返回 result 和 err
    }
    result = a / b
    return  // 等价于 return result, err
}

// 更复杂的示例
func rectInfo(width, height float64) (area, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return
}

func main() {
    a, p := rectInfo(5, 3)
    fmt.Printf("面积: %.2f, 周长: %.2f\n", a, p)
}

可变参数函数

// ...表示可变参数,类型为切片
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

func main() {
    fmt.Println(sum())              // 0
    fmt.Println(sum(1))             // 1
    fmt.Println(sum(1, 2, 3))       // 6

    // 展开切片
    numbers := []int{1, 2, 3, 4, 5}
    fmt.Println(sum(numbers...))    // 15
}

// 可变参数必须是最后一个参数
func printf(format string, args ...interface{}) {
    fmt.Printf(format, args...)
}

函数作为值

// 函数可以赋值给变量
func add(a, b int) int {
    return a + b
}

func main() {
    // 函数类型: func(int, int) int
    var f func(int, int) int
    f = add
    fmt.Println(f(3, 4))  // 7

    // 匿名函数
    multiply := func(a, b int) int {
        return a * b
    }
    fmt.Println(multiply(3, 4))  // 12

    // 立即调用
    result := func(x int) int {
        return x * x
    }(5)
    fmt.Println(result)  // 25
}

高阶函数

// 函数作为参数
func apply(f func(int, int) int, a, b int) int {
    return f(a, b)
}

func add(a, b int) int { return a + b }
func mul(a, b int) int { return a * b }

func main() {
    fmt.Println(apply(add, 3, 4))  // 7
    fmt.Println(apply(mul, 3, 4))  // 12
}

// 函数作为返回值
func makeAdder(x int) func(int) int {
    return func(y int) int {
        return x + y
    }
}

func main() {
    add5 := makeAdder(5)
    add10 := makeAdder(10)

    fmt.Println(add5(3))   // 8
    fmt.Println(add10(3))  // 13
}

闭包

闭包是引用了自由变量的函数。

// 计数器闭包
func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counter1 := makeCounter()
    counter2 := makeCounter()

    fmt.Println(counter1())  // 1
    fmt.Println(counter1())  // 2
    fmt.Println(counter1())  // 3

    fmt.Println(counter2())  // 1
    fmt.Println(counter2())  // 2
}

// 实用示例: 斐波那契生成器
func fibonacci() func() int {
    a, b := 0, 1
    return func() int {
        a, b = b, a+b
        return a
    }
}

func main() {
    fib := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Print(fib(), " ")
    }
    // 输出: 1 1 2 3 5 8 13 21 34 55
}

递归

// 阶乘
func factorial(n int) int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n-1)
}

// 斐波那契(低效递归版本)
func fib(n int) int {
    if n <= 1 {
        return n
    }
    return fib(n-1) + fib(n-2)
}

// 二分查找(递归)
func binarySearch(arr []int, target, low, high int) int {
    if low > high {
        return -1  // 未找到
    }

    mid := (low + high) / 2
    if arr[mid] == target {
        return mid
    } else if arr[mid] > target {
        return binarySearch(arr, target, low, mid-1)
    } else {
        return binarySearch(arr, target, mid+1, high)
    }
}

init 函数

每个包可以有多个 init 函数,在程序启动时自动执行。

package main

import "fmt"

var globalVar int

// init 在 main 之前执行
func init() {
    fmt.Println("init 1")
    globalVar = 100
}

func init() {
    fmt.Println("init 2")
}

func main() {
    fmt.Println("main")
    fmt.Println(globalVar)
}

// 输出:
// init 1
// init 2
// main
// 100

执行顺序: 导入包的 init → 当前包的变量初始化 → 当前包的 init → main

复合数据类型

数组

数组是固定长度的相同类型元素序列。

声明和初始化

// 声明
var arr [5]int              // [0 0 0 0 0]

// 初始化
var arr1 = [5]int{1, 2, 3, 4, 5}
arr2 := [3]string{"a", "b", "c"}

// 自动推导长度
arr3 := [...]int{1, 2, 3, 4}  // 长度为 4

// 指定索引初始化
arr4 := [5]int{1: 10, 3: 30}  // [0 10 0 30 0]

// 访问和修改
arr1[0] = 100
fmt.Println(arr1[2])

// 长度
length := len(arr1)

数组遍历

arr := [5]int{1, 2, 3, 4, 5}

// 方式 1: for + index
for i := 0; i < len(arr); i++ {
    fmt.Println(arr[i])
}

// 方式 2: range
for index, value := range arr {
    fmt.Printf("arr[%d] = %d\n", index, value)
}

// 只要值
for _, v := range arr {
    fmt.Println(v)
}

数组是值类型

// 数组赋值会拷贝整个数组
arr1 := [3]int{1, 2, 3}
arr2 := arr1        // 完整拷贝
arr2[0] = 999
fmt.Println(arr1)   // [1 2 3]
fmt.Println(arr2)   // [999 2 3]

// 函数传参也是拷贝
func modifyArray(arr [3]int) {
    arr[0] = 100  // 不影响原数组
}

切片 (Slice)

切片是对数组的引用,长度可变,是 Go 中最常用的数据结构。

切片的本质

// 切片包含三个字段:
// - 指向底层数组的指针
// - 长度 (len)
// - 容量 (cap)

创建切片

// 方式 1: 字面量
slice1 := []int{1, 2, 3, 4, 5}

// 方式 2: make
slice2 := make([]int, 5)       // 长度和容量都是 5
slice3 := make([]int, 3, 5)    // 长度 3, 容量 5

// 方式 3: 从数组切片
arr := [5]int{1, 2, 3, 4, 5}
slice4 := arr[1:4]             // [2 3 4]
slice5 := arr[:3]              // [1 2 3]
slice6 := arr[2:]              // [3 4 5]
slice7 := arr[:]               // [1 2 3 4 5]

// nil 切片
var slice8 []int               // nil, len=0, cap=0

切片操作

slice := []int{1, 2, 3, 4, 5}

// 访问
fmt.Println(slice[2])          // 3

// 修改
slice[2] = 999

// 长度和容量
fmt.Println(len(slice))        // 5
fmt.Println(cap(slice))        // 5

// 追加元素
slice = append(slice, 6)       // [1 2 999 4 5 6]
slice = append(slice, 7, 8, 9) // 追加多个
slice = append(slice, []int{10, 11}...)  // 追加切片

// 拷贝
dest := make([]int, len(slice))
copy(dest, slice)

// 删除元素(通过切片)
// 删除索引 2 的元素
slice = append(slice[:2], slice[3:]...)

// 插入元素
// 在索引 2 插入 100
slice = append(slice[:2], append([]int{100}, slice[2:]...)...)

切片是引用类型

slice1 := []int{1, 2, 3}
slice2 := slice1        // 共享底层数组
slice2[0] = 999
fmt.Println(slice1)     // [999 2 3]

// 函数传参
func modifySlice(s []int) {
    s[0] = 100          // 会影响原切片
}

func main() {
    slice := []int{1, 2, 3}
    modifySlice(slice)
    fmt.Println(slice)  // [100 2 3]
}

切片的扩容

// 当容量不足时,append 会自动扩容(创建新数组)
slice := make([]int, 0, 2)
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))  // len=0 cap=2

slice = append(slice, 1)
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))  // len=1 cap=2

slice = append(slice, 2)
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))  // len=2 cap=2

slice = append(slice, 3)  // 触发扩容
fmt.Printf("len=%d cap=%d\n", len(slice), cap(slice))  // len=3 cap=4

扩容策略:

  • 容量 < 1024: 翻倍
  • 容量 >= 1024: 增长 25%

二维切片

// 创建二维切片
matrix := [][]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

// 动态创建
rows, cols := 3, 4
matrix2 := make([][]int, rows)
for i := range matrix2 {
    matrix2[i] = make([]int, cols)
}

// 访问
fmt.Println(matrix[1][2])  // 6

Map (映射)

Map 是无序的键值对集合,类似其他语言的哈希表或字典。

创建 Map

// 方式 1: 字面量
ages := map[string]int{
    "Alice": 25,
    "Bob":   30,
    "Carol": 28,
}

// 方式 2: make
scores := make(map[string]float64)

// 方式 3: 声明(nil map,不能直接使用)
var m map[string]int  // nil
// m["key"] = 1       // panic!
m = make(map[string]int)  // 必须初始化

Map 操作

ages := map[string]int{
    "Alice": 25,
    "Bob":   30,
}

// 添加/修改
ages["Carol"] = 28
ages["Alice"] = 26  // 修改

// 访问
age := ages["Alice"]

// 检查键是否存在
age, exists := ages["David"]
if exists {
    fmt.Println("David 的年龄:", age)
} else {
    fmt.Println("David 不存在")
}

// 删除
delete(ages, "Bob")

// 长度
length := len(ages)

// 清空 map
ages = make(map[string]int)  // 或重新赋值为 nil

遍历 Map

ages := map[string]int{
    "Alice": 25,
    "Bob":   30,
    "Carol": 28,
}

// 遍历键值对(无序!)
for name, age := range ages {
    fmt.Printf("%s: %d\n", name, age)
}

// 只遍历键
for name := range ages {
    fmt.Println(name)
}

// 只遍历值
for _, age := range ages {
    fmt.Println(age)
}

注意: Map 的遍历顺序是随机的!

Map 的键类型

// 键必须是可比较类型
// ✅ 可以: int, string, pointer, struct(字段可比较), array
// ❌ 不可以: slice, map, function

// 示例: struct 作为键
type Point struct {
    X, Y int
}

distances := map[Point]float64{
    {0, 0}: 0,
    {1, 1}: 1.414,
    {2, 2}: 2.828,
}

Map 是引用类型

m1 := map[string]int{"a": 1}
m2 := m1        // m2 和 m1 共享底层数据
m2["a"] = 999
fmt.Println(m1) // map[a:999]

// 函数传参
func modifyMap(m map[string]int) {
    m["key"] = 100  // 会影响原 map
}

结构体 (Struct)

结构体是将零个或多个字段组合在一起的复合数据类型。

定义和初始化

// 定义结构体
type Person struct {
    Name string
    Age  int
    City string
}

// 初始化方式 1: 字段名初始化(推荐)
p1 := Person{
    Name: "Alice",
    Age:  25,
    City: "北京",
}

// 初始化方式 2: 按顺序(不推荐,易错)
p2 := Person{"Bob", 30, "上海"}

// 初始化方式 3: 部分字段(其他为零值)
p3 := Person{Name: "Carol"}

// 零值
var p4 Person  // {"" 0 ""}

// 指针
p5 := &Person{Name: "David", Age: 35}

访问和修改字段

p := Person{Name: "Alice", Age: 25}

// 访问
fmt.Println(p.Name)
fmt.Println(p.Age)

// 修改
p.Age = 26
p.City = "深圳"

// 指针自动解引用
ptr := &p
ptr.Name = "Bob"  // 等价于 (*ptr).Name = "Bob"

匿名字段(嵌入)

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person      // 匿名字段(嵌入)
    Company string
    Salary  float64
}

func main() {
    e := Employee{
        Person:  Person{Name: "Alice", Age: 30},
        Company: "Google",
        Salary:  100000,
    }

    // 直接访问嵌入字段
    fmt.Println(e.Name)     // 等价于 e.Person.Name
    fmt.Println(e.Age)
    fmt.Println(e.Company)
}

结构体标签 (Tags)

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Age   int    `json:"-"`  // 忽略此字段
}

func main() {
    u := User{Name: "Alice", Email: "alice@example.com", Age: 25}

    // 序列化为 JSON
    data, _ := json.Marshal(u)
    fmt.Println(string(data))  // {"name":"Alice","email":"alice@example.com"}
}

结构体比较

type Point struct {
    X, Y int
}

p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{2, 3}

fmt.Println(p1 == p2)  // true
fmt.Println(p1 == p3)  // false

// 包含不可比较字段的结构体不能比较
type Data struct {
    Values []int  // slice 不可比较
}
// d1 := Data{[]int{1, 2}}
// d2 := Data{[]int{1, 2}}
// fmt.Println(d1 == d2)  // 编译错误!

方法和接口

方法

方法是与特定类型关联的函数。

定义方法

type Rectangle struct {
    Width, Height float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 指针接收者方法
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}

    fmt.Println(rect.Area())  // 50

    rect.Scale(2)
    fmt.Println(rect.Area())  // 200
}

值接收者 vs 指针接收者

type Counter struct {
    count int
}

// 值接收者: 不能修改原对象
func (c Counter) Increment() {
    c.count++  // 只修改副本
}

// 指针接收者: 可以修改原对象
func (c *Counter) IncrementPtr() {
    c.count++
}

func main() {
    c := Counter{count: 0}

    c.Increment()
    fmt.Println(c.count)  // 0 (未改变)

    c.IncrementPtr()
    fmt.Println(c.count)  // 1 (已改变)
}

选择指针接收者的情况:

  1. 需要修改接收者
  2. 接收者是大型结构体(避免拷贝)
  3. 保持一致性(如果某些方法用指针,其他也应该用)

方法可以定义在任何类型上

// 自定义类型
type MyInt int

func (m MyInt) Double() MyInt {
    return m * 2
}

func main() {
    var n MyInt = 5
    fmt.Println(n.Double())  // 10
}

// 不能在内置类型或其他包的类型上定义方法
// func (i int) Double() int { ... }  // 错误!

接口

接口是一组方法签名的集合,定义了对象的行为。

定义接口

// 接口定义
type Shape interface {
    Area() float64
    Perimeter() float64
}

// 实现接口(隐式)
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

// 使用接口
func printShapeInfo(s Shape) {
    fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 5}

    printShapeInfo(rect)
    printShapeInfo(circle)
}

空接口

// interface{} 或 any (Go 1.18+) 可以接受任何类型
func printAnything(v interface{}) {
    fmt.Println(v)
}

func main() {
    printAnything(42)
    printAnything("hello")
    printAnything([]int{1, 2, 3})
}

// Go 1.18+ 可以用 any
func printAny(v any) {
    fmt.Println(v)
}

类型断言

func describe(i interface{}) {
    // 类型断言
    v, ok := i.(string)
    if ok {
        fmt.Printf("字符串: %s\n", v)
    } else {
        fmt.Println("不是字符串")
    }

    // 类型 switch
    switch v := i.(type) {
    case int:
        fmt.Printf("整数: %d\n", v)
    case string:
        fmt.Printf("字符串: %s\n", v)
    case bool:
        fmt.Printf("布尔: %t\n", v)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
}

func main() {
    describe(42)
    describe("hello")
    describe(true)
}

常用标准接口

// 1. fmt.Stringer
type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}

func main() {
    p := Person{"Alice", 25}
    fmt.Println(p)  // Alice (25岁)
}

// 2. error 接口
type MyError struct {
    Message string
    Code    int
}

func (e MyError) Error() string {
    return fmt.Sprintf("错误 %d: %s", e.Code, e.Message)
}

// 3. io.Reader 和 io.Writer
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 4. sort.Interface
type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

接口组合

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type Closer interface {
    Close() error
}

// 组合接口
type ReadWriter interface {
    Reader
    Writer
}

type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

并发编程

Go 的并发模型基于 CSP (Communicating Sequential Processes),通过 goroutine 和 channel 实现。

Goroutine

Goroutine 是轻量级线程,由 Go 运行时管理。

创建 Goroutine

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello from goroutine!")
}

func main() {
    // 启动 goroutine
    go sayHello()

    // 匿名函数
    go func() {
        fmt.Println("匿名 goroutine")
    }()

    // 主程序需要等待,否则会立即退出
    time.Sleep(1 * time.Second)
    fmt.Println("Main function")
}

Goroutine 示例

func count(name string) {
    for i := 1; i <= 5; i++ {
        fmt.Printf("%s: %d\n", name, i)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go count("goroutine 1")
    go count("goroutine 2")

    time.Sleep(1 * time.Second)
    fmt.Println("Done")
}

Channel

Channel 是 goroutine 之间通信的管道。

创建和使用 Channel

// 创建 channel
ch := make(chan int)

// 发送数据(会阻塞直到有接收者)
ch <- 42

// 接收数据(会阻塞直到有数据)
value := <-ch

// 关闭 channel
close(ch)

// 检查 channel 是否关闭
value, ok := <-ch
if !ok {
    fmt.Println("Channel 已关闭")
}

基本示例

func sum(a, b int, result chan int) {
    result <- a + b
}

func main() {
    result := make(chan int)

    go sum(3, 4, result)

    // 等待结果
    sum := <-result
    fmt.Println("Sum:", sum)
}

缓冲 Channel

// 无缓冲 channel(同步)
ch1 := make(chan int)

// 缓冲 channel(异步)
ch2 := make(chan int, 3)  // 容量为 3

func main() {
    ch := make(chan int, 2)

    // 不会阻塞,因为有缓冲
    ch <- 1
    ch <- 2

    // 第三次发送会阻塞(缓冲已满)
    // ch <- 3

    fmt.Println(<-ch)  // 1
    fmt.Println(<-ch)  // 2
}

单向 Channel

// 只发送 channel
func send(ch chan<- int) {
    ch <- 42
}

// 只接收 channel
func receive(ch <-chan int) {
    value := <-ch
    fmt.Println(value)
}

func main() {
    ch := make(chan int)

    go send(ch)
    receive(ch)
}

range 和 close

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c)  // 必须关闭,否则 range 会永久阻塞
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)

    for num := range c {
        fmt.Println(num)
    }
}

Select

Select 用于处理多个 channel 操作。

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "来自 ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "来自 ch2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

Select 的 default 分支

func main() {
    ch := make(chan int)

    select {
    case value := <-ch:
        fmt.Println("接收到:", value)
    default:
        fmt.Println("没有数据,不阻塞")
    }
}

超时控制

func main() {
    ch := make(chan int)

    go func() {
        time.Sleep(2 * time.Second)
        ch <- 42
    }()

    select {
    case value := <-ch:
        fmt.Println("接收到:", value)
    case <-time.After(1 * time.Second):
        fmt.Println("超时!")
    }
}

WaitGroup

用于等待一组 goroutine 完成。

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()  // 完成时调用

    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)  // 增加计数
        go worker(i, &wg)
    }

    wg.Wait()  // 等待所有 goroutine 完成
    fmt.Println("All workers done")
}

Mutex (互斥锁)

用于保护共享资源。

import (
    "fmt"
    "sync"
)

type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

func main() {
    counter := SafeCounter{}
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Increment()
        }()
    }

    wg.Wait()
    fmt.Println("Final count:", counter.Value())  // 1000
}

RWMutex (读写锁)

import "sync"

type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) (string, bool) {
    c.mu.RLock()  // 读锁
    defer c.mu.RUnlock()
    value, ok := c.data[key]
    return value, ok
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()   // 写锁
    defer c.mu.Unlock()
    c.data[key] = value
}

并发模式

Worker Pool

func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job)
        time.Sleep(time.Second)
        results <- job * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // 启动 3 个 worker
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // 发送 5 个任务
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    // 收集结果
    for a := 1; a <= 5; a++ {
        result := <-results
        fmt.Println("Result:", result)
    }
}

Fan-out, Fan-in

func producer(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}

func merge(channels ...<-chan int) <-chan int {
    var wg sync.WaitGroup
    out := make(chan int)

    output := func(c <-chan int) {
        defer wg.Done()
        for n := range c {
            out <- n
        }
    }

    wg.Add(len(channels))
    for _, c := range channels {
        go output(c)
    }

    go func() {
        wg.Wait()
        close(out)
    }()

    return out
}

func main() {
    in := producer(1, 2, 3, 4, 5)

    // Fan-out
    c1 := square(in)
    c2 := square(in)

    // Fan-in
    for n := range merge(c1, c2) {
        fmt.Println(n)
    }
}

Context (上下文控制)

import (
    "context"
    "fmt"
    "time"
)

func work(ctx context.Context, name string) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("%s: 收到取消信号\n", name)
            return
        default:
            fmt.Printf("%s: 工作中...\n", name)
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    // 1. 带超时的 context
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    go work(ctx, "Worker1")

    // 2. 可取消的 context
    ctx2, cancel2 := context.WithCancel(context.Background())
    go work(ctx2, "Worker2")

    time.Sleep(1 * time.Second)
    cancel2()  // 手动取消

    time.Sleep(3 * time.Second)
}

错误处理

Go 使用显式错误值而不是异常。

基本错误处理

import (
    "errors"
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为0")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("错误:", err)
        return
    }
    fmt.Println("结果:", result)
}

自定义错误

// 方式 1: 实现 error 接口
type DivideError struct {
    Dividend float64
    Divisor  float64
}

func (e *DivideError) Error() string {
    return fmt.Sprintf("不能将 %.2f 除以 %.2f", e.Dividend, e.Divisor)
}

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, &DivideError{Dividend: a, Divisor: b}
    }
    return a / b, nil
}

// 方式 2: 使用 fmt.Errorf
func openFile(filename string) error {
    return fmt.Errorf("无法打开文件: %s", filename)
}

错误包装 (Go 1.13+)

import (
    "errors"
    "fmt"
)

func readConfig() error {
    return errors.New("配置文件格式错误")
}

func initialize() error {
    err := readConfig()
    if err != nil {
        return fmt.Errorf("初始化失败: %w", err)  // 包装错误
    }
    return nil
}

func main() {
    err := initialize()
    if err != nil {
        fmt.Println(err)  // 初始化失败: 配置文件格式错误

        // 检查原始错误
        if errors.Is(err, errors.New("配置文件格式错误")) {
            fmt.Println("是配置错误")
        }

        // 解包错误
        unwrapped := errors.Unwrap(err)
        fmt.Println(unwrapped)
    }
}

错误检查模式

// 哨兵错误
var (
    ErrNotFound = errors.New("未找到")
    ErrInvalid  = errors.New("无效参数")
)

func find(id int) error {
    if id < 0 {
        return ErrInvalid
    }
    return ErrNotFound
}

func main() {
    err := find(-1)
    if errors.Is(err, ErrInvalid) {
        fmt.Println("参数错误")
    }
}

// 类型断言
type ValidationError struct {
    Field string
    Msg   string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}

func validate() error {
    return &ValidationError{Field: "email", Msg: "格式错误"}
}

func main() {
    err := validate()

    var validErr *ValidationError
    if errors.As(err, &validErr) {
        fmt.Printf("字段 %s 验证失败: %s\n", validErr.Field, validErr.Msg)
    }
}

Panic 和 Recover

// panic: 用于不可恢复的错误
func mustOpen(filename string) *os.File {
    f, err := os.Open(filename)
    if err != nil {
        panic(err)  // 程序会崩溃
    }
    return f
}

// recover: 捕获 panic
func safeFunction() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("捕获到 panic:", r)
        }
    }()

    // 可能 panic 的代码
    panic("出错了!")

    fmt.Println("这行不会执行")
}

func main() {
    safeFunction()
    fmt.Println("程序继续运行")
}

最佳实践:

  • 库代码不应 panic,应返回错误
  • 只在程序无法继续运行时使用 panic
  • defer + recover 用于库的边界保护

包和模块

包的基础

包的组织

myproject/
├── go.mod
├── main.go
├── utils/
│   ├── math.go
│   └── string.go
└── models/
    └── user.go

定义包

// utils/math.go
package utils

// 导出函数(首字母大写)
func Add(a, b int) int {
    return a + b
}

// 未导出函数(首字母小写)
func subtract(a, b int) int {
    return a - b
}

// 导出常量
const Pi = 3.14159

// 未导出常量
const maxSize = 100

使用包

// main.go
package main

import (
    "fmt"
    "myproject/utils"
)

func main() {
    result := utils.Add(3, 4)
    fmt.Println(result)

    // fmt.Println(utils.subtract(5, 2))  // 错误: 未导出
}

Go Modules

Go 1.11+ 使用 Go Modules 管理依赖。

初始化模块

# 创建新模块
go mod init github.com/username/myproject

# 生成 go.mod 文件

go.mod 文件

module github.com/username/myproject

go 1.21

require (
    github.com/gin-gonic/gin v1.9.0
    github.com/spf13/cobra v1.7.0
)

replace github.com/old/package => github.com/new/package v1.0.0

常用命令

# 添加依赖
go get github.com/gin-gonic/gin

# 添加特定版本
go get github.com/gin-gonic/gin@v1.9.0

# 更新依赖
go get -u github.com/gin-gonic/gin

# 更新所有依赖
go get -u ./...

# 整理依赖(移除未使用的)
go mod tidy

# 下载依赖到本地缓存
go mod download

# 将依赖复制到 vendor 目录
go mod vendor

# 查看依赖图
go mod graph

# 验证依赖
go mod verify

包的初始化

package mypackage

import "fmt"

// 包级变量
var GlobalVar = initVar()

func initVar() int {
    fmt.Println("初始化 GlobalVar")
    return 100
}

// init 函数(自动执行)
func init() {
    fmt.Println("init 1")
}

func init() {
    fmt.Println("init 2")
}

// 执行顺序:
// 1. 导入的包的 init
// 2. 当前包的变量初始化
// 3. 当前包的 init 函数
// 4. main 函数

内部包 (internal)

myproject/
├── internal/
│   └── config/
│       └── config.go  # 只能被 myproject 导入
└── main.go
// internal/config/config.go
package config

type Config struct {
    Port int
}

// 其他项目无法导入 myproject/internal/config

标准库精选

fmt - 格式化 I/O

import "fmt"

// 打印
fmt.Print("Hello")           // 不换行
fmt.Println("Hello")         // 换行
fmt.Printf("数字: %d\n", 42) // 格式化

// 格式化字符串
s := fmt.Sprintf("数字: %d", 42)

// 扫描输入
var name string
var age int
fmt.Scan(&name, &age)
fmt.Scanf("%s %d", &name, &age)

// 常用格式化动词
// %v    默认格式
// %+v   包含字段名
// %#v   Go 语法表示
// %T    类型
// %t    布尔
// %d    十进制整数
// %b    二进制
// %o    八进制
// %x    十六进制
// %f    浮点数
// %s    字符串
// %p    指针

strings - 字符串操作

import "strings"

s := "Hello, World"

// 包含
strings.Contains(s, "World")     // true

// 统计
strings.Count(s, "l")            // 3

// 前缀/后缀
strings.HasPrefix(s, "Hello")   // true
strings.HasSuffix(s, "World")   // true

// 索引
strings.Index(s, "World")        // 7
strings.LastIndex(s, "l")        // 10

// 替换
strings.Replace(s, "World", "Go", 1)    // Hello, Go
strings.ReplaceAll(s, "l", "L")         // HeLLo, WorLd

// 分割
strings.Split("a,b,c", ",")      // []string{"a", "b", "c"}

// 连接
strings.Join([]string{"a", "b"}, ",")  // "a,b"

// 大小写
strings.ToUpper(s)               // HELLO, WORLD
strings.ToLower(s)               // hello, world

// 去空格
strings.TrimSpace("  hello  ")  // "hello"
strings.Trim("xxhelloxx", "x")  // "hello"

strconv - 类型转换

import "strconv"

// 字符串 → 数字
i, err := strconv.Atoi("42")           // int
f, err := strconv.ParseFloat("3.14", 64)
b, err := strconv.ParseBool("true")

// 数字 → 字符串
s := strconv.Itoa(42)
s = strconv.FormatFloat(3.14, 'f', 2, 64)
s = strconv.FormatBool(true)

time - 时间处理

import "time"

// 当前时间
now := time.Now()
fmt.Println(now)

// 格式化(Magic time: Mon Jan 2 15:04:05 MST 2006)
formatted := now.Format("2006-01-02 15:04:05")
fmt.Println(formatted)

// 解析
t, err := time.Parse("2006-01-02", "2025-11-03")

// 时间运算
future := now.Add(24 * time.Hour)        // 加一天
past := now.Add(-1 * time.Hour)          // 减一小时

// 时间差
duration := future.Sub(now)
fmt.Println(duration.Hours())            // 24

// 睡眠
time.Sleep(2 * time.Second)

// 定时器
timer := time.NewTimer(2 * time.Second)
<-timer.C  // 等待

// 周期执行
ticker := time.NewTicker(1 * time.Second)
for i := 0; i < 5; i++ {
    <-ticker.C
    fmt.Println("Tick")
}
ticker.Stop()

os - 操作系统接口

import "os"

// 环境变量
os.Setenv("MY_VAR", "value")
value := os.Getenv("MY_VAR")
os.Unsetenv("MY_VAR")

// 命令行参数
args := os.Args  // []string

// 工作目录
dir, err := os.Getwd()
os.Chdir("/tmp")

// 文件操作
file, err := os.Create("test.txt")
file.Write([]byte("Hello"))
file.Close()

os.Remove("test.txt")
os.Rename("old.txt", "new.txt")

// 目录操作
os.Mkdir("mydir", 0755)
os.MkdirAll("path/to/dir", 0755)
os.RemoveAll("path/to/dir")

// 文件信息
info, err := os.Stat("file.txt")
if err != nil {
    if os.IsNotExist(err) {
        fmt.Println("文件不存在")
    }
}
fmt.Println(info.Size())
fmt.Println(info.IsDir())

io - I/O 接口

import (
    "io"
    "os"
    "strings"
)

// 复制
src, _ := os.Open("source.txt")
dst, _ := os.Create("dest.txt")
io.Copy(dst, src)

// 读取全部
data, err := io.ReadAll(src)

// Reader 示例
reader := strings.NewReader("Hello, World!")
buf := make([]byte, 5)
n, err := reader.Read(buf)
fmt.Printf("读取 %d 字节: %s\n", n, buf[:n])

// Writer 示例
var builder strings.Builder
builder.WriteString("Hello")
builder.WriteString(" World")
result := builder.String()

json - JSON 编解码

import "encoding/json"

type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email,omitempty"`
}

// 序列化
p := Person{Name: "Alice", Age: 25}
data, err := json.Marshal(p)
fmt.Println(string(data))  // {"name":"Alice","age":25}

// 格式化输出
data, err = json.MarshalIndent(p, "", "  ")

// 反序列化
jsonStr := `{"name":"Bob","age":30}`
var p2 Person
err = json.Unmarshal([]byte(jsonStr), &p2)

// 使用 Decoder/Encoder (流式)
file, _ := os.Open("data.json")
decoder := json.NewDecoder(file)
var result map[string]interface{}
decoder.Decode(&result)

http - HTTP 客户端和服务器

import (
    "fmt"
    "io"
    "net/http"
)

// HTTP 服务器
func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

// HTTP 客户端
resp, err := http.Get("https://api.example.com/data")
if err != nil {
    panic(err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
fmt.Println(string(body))

// POST 请求
data := strings.NewReader(`{"key":"value"}`)
resp, err = http.Post(
    "https://api.example.com",
    "application/json",
    data,
)

regexp - 正则表达式

import "regexp"

// 编译正则
re := regexp.MustCompile(`\d+`)

// 匹配
matched := re.MatchString("abc123")  // true

// 查找
result := re.FindString("abc123def456")  // "123"
results := re.FindAllString("abc123def456", -1)  // ["123", "456"]

// 替换
replaced := re.ReplaceAllString("abc123def456", "X")  // "abcXdefX"

// 分组
re2 := regexp.MustCompile(`(\w+)@(\w+)\.com`)
matches := re2.FindStringSubmatch("user@example.com")
// matches = ["user@example.com", "user", "example"]

sort - 排序

import "sort"

// 基本类型排序
ints := []int{3, 1, 4, 1, 5}
sort.Ints(ints)  // [1 1 3 4 5]

strs := []string{"c", "a", "b"}
sort.Strings(strs)  // [a b c]

// 自定义排序
type Person struct {
    Name string
    Age  int
}

people := []Person{
    {"Alice", 25},
    {"Bob", 30},
    {"Carol", 20},
}

sort.Slice(people, func(i, j int) bool {
    return people[i].Age < people[j].Age
})

// 实现 sort.Interface
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

sort.Sort(ByAge(people))

// 二分查找
nums := []int{1, 2, 3, 4, 5}
index := sort.SearchInts(nums, 3)  // 2

最佳实践

代码风格

命名规范

#+BEGINSRC go // 包名: 小写单词,不使用下划线 package http package strings

// 变量/函数: 驼峰命名 var userName string func getUserInfo() {}

// 常量: 驼峰命名或全大写 const MaxSize = 100 const MAXSIZE = 100

// 导出: 首字母大写 type User struct {}

Comments:

Email questions, comments, and corrections to hi@smartisan.dev.

Submissions may appear publicly on this website, unless requested otherwise in your email.