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 (已改变)
}
选择指针接收者的情况:
- 需要修改接收者
- 接收者是大型结构体(避免拷贝)
- 保持一致性(如果某些方法用指针,其他也应该用)
方法可以定义在任何类型上
// 自定义类型
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.