Add Chapter 4: Functions and Interfaces - Closures, Defer, Panic/Recover, and Interfaces (Deep Dive)
This commit is contained in:
895
chapters/chapter-4-functions-interfaces.md
Normal file
895
chapters/chapter-4-functions-interfaces.md
Normal file
@@ -0,0 +1,895 @@
|
||||
# 第四章:函数与接口 —— 代码复用与多态
|
||||
|
||||
> **本章目标**:深入理解 Go 的函数式编程特性(闭包、匿名函数)、defer 机制、错误处理、panic/recover,以及接口的底层实现原理和最佳实践。
|
||||
|
||||
## 4.1 函数基础回顾与深度解析
|
||||
|
||||
### 4.1.1 函数声明与调用
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 基本函数
|
||||
func add(a int, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// 省略参数类型(连续同类型)
|
||||
func addShort(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// 多个返回值
|
||||
func divide(a, b float64) (float64, error) {
|
||||
if b == 0 {
|
||||
return 0, fmt.Errorf("除数不能为零")
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
|
||||
// 命名返回值
|
||||
func divideNamed(a, b float64) (result float64, err error) {
|
||||
if b == 0 {
|
||||
err = fmt.Errorf("除数不能为零")
|
||||
return // 返回命名返回值
|
||||
}
|
||||
result = a / b
|
||||
return // 返回命名返回值
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(add(3, 4))
|
||||
fmt.Println(addShort(3, 4))
|
||||
|
||||
result, err := divide(10, 2)
|
||||
if err != nil {
|
||||
fmt.Println("错误:", err)
|
||||
} else {
|
||||
fmt.Printf("10 / 2 = %.2f\n", result)
|
||||
}
|
||||
|
||||
result2, _ := divideNamed(20, 4)
|
||||
fmt.Printf("20 / 4 = %.2f\n", result2)
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- **命名返回值**:适合返回值较多或需要明确语义的场景
|
||||
- **省略类型**:仅适用于连续的同类型参数
|
||||
- **多返回值**:Go 的特色,常用于返回结果 + 错误
|
||||
|
||||
### 4.1.2 可变参数
|
||||
|
||||
```go
|
||||
func sum(nums ...int) int {
|
||||
total := 0
|
||||
for _, n := range nums {
|
||||
total += n
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func printArgs(prefix string, args ...interface{}) {
|
||||
fmt.Printf("%s: ", prefix)
|
||||
for _, arg := range args {
|
||||
fmt.Printf("%v ", arg)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
|
||||
fmt.Println(sum()) // 0
|
||||
|
||||
// 切片展开
|
||||
nums := []int{10, 20, 30}
|
||||
fmt.Println(sum(nums...)) // 60
|
||||
|
||||
printArgs("参数", 1, "hello", 3.14, true)
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `...T` 表示可变参数,在函数内部被视为**切片**
|
||||
- 调用时可以用 `...` 展开切片
|
||||
- 可变参数必须是**最后一个参数**
|
||||
|
||||
---
|
||||
|
||||
## 4.2 匿名函数与闭包
|
||||
|
||||
### 4.2.1 匿名函数
|
||||
|
||||
```go
|
||||
func anonymousFunc() {
|
||||
// 定义匿名函数
|
||||
func() {
|
||||
fmt.Println("这是一个匿名函数")
|
||||
}() // 立即调用
|
||||
|
||||
// 赋值给变量
|
||||
multiply := func(a, b int) int {
|
||||
return a * b
|
||||
}
|
||||
fmt.Println(multiply(3, 4)) // 12
|
||||
|
||||
// 作为参数传递
|
||||
apply := func(f func(int, int) int, x, y int) int {
|
||||
return f(x, y)
|
||||
}
|
||||
fmt.Println(apply(add, 5, 6)) // 11
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2.2 闭包(Closure)⭐ 核心重点
|
||||
|
||||
闭包是**引用了外部变量的匿名函数**。闭包不仅包含函数本身,还包含其**创建时的环境**。
|
||||
|
||||
```go
|
||||
func closureExample() {
|
||||
// 创建计数器
|
||||
counter := func() func() int {
|
||||
count := 0
|
||||
return func() int {
|
||||
count++
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
c1 := counter()
|
||||
c2 := counter()
|
||||
|
||||
fmt.Println(c1()) // 1
|
||||
fmt.Println(c1()) // 2
|
||||
fmt.Println(c1()) // 3
|
||||
fmt.Println(c2()) // 1(独立的 count)
|
||||
fmt.Println(c1()) // 4
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 闭包**捕获变量**而非值:内部函数引用的是外部变量的**引用**
|
||||
- 每次调用 `counter()` 都会创建**新的 `count` 变量**
|
||||
- 闭包的生命周期**长于**外部函数
|
||||
|
||||
### 4.2.3 闭包的陷阱
|
||||
|
||||
#### 陷阱 1:循环中的闭包
|
||||
|
||||
```go
|
||||
func closureLoopBug() {
|
||||
funcs := make([]func(), 3)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
funcs[i] = func() {
|
||||
fmt.Println(i) // 捕获的是 i 的引用
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range funcs {
|
||||
f() // 输出:3 3 3(循环结束后的 i 值)
|
||||
}
|
||||
}
|
||||
|
||||
// 正确做法
|
||||
func closureLoopFix() {
|
||||
funcs := make([]func(), 3)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
i := i // 创建新的变量
|
||||
funcs[i] = func() {
|
||||
fmt.Println(i)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range funcs {
|
||||
f() // 输出:0 1 2
|
||||
}
|
||||
}
|
||||
|
||||
// 或者使用参数
|
||||
func closureLoopFix2() {
|
||||
funcs := make([]func(), 3)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
funcs[i] = func(n int) {
|
||||
fmt.Println(n)
|
||||
}(i)
|
||||
}
|
||||
|
||||
for _, f := range funcs {
|
||||
f() // 输出:0 1 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 陷阱 2:闭包内存泄漏
|
||||
|
||||
```go
|
||||
func closureMemoryLeak() {
|
||||
largeData := make([]byte, 1024*1024) // 1MB
|
||||
|
||||
smallFunc := func() {
|
||||
fmt.Println(len(largeData))
|
||||
}
|
||||
|
||||
// 即使 largeData 不再需要,只要 smallFunc 存在,largeData 就不会被回收
|
||||
_ = smallFunc
|
||||
}
|
||||
```
|
||||
|
||||
**最佳实践**:
|
||||
- 如果闭包不需要引用大对象,避免捕获
|
||||
- 使用 `func(n int)` 参数传递值而非捕获引用
|
||||
|
||||
### 4.2.4 闭包的实战应用
|
||||
|
||||
#### 1. 工厂函数
|
||||
|
||||
```go
|
||||
func createMultiplier(factor int) func(int) int {
|
||||
return func(x int) int {
|
||||
return x * factor
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
double := createMultiplier(2)
|
||||
triple := createMultiplier(3)
|
||||
|
||||
fmt.Println(double(5)) // 10
|
||||
fmt.Println(triple(5)) // 15
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 中间件模式
|
||||
|
||||
```go
|
||||
type Handler func(string) string
|
||||
|
||||
func loggingMiddleware(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
fmt.Printf("处理:%s\n", input)
|
||||
result := next(input)
|
||||
fmt.Printf("结果:%s\n", result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func process(input string) string {
|
||||
return "处理完成:" + input
|
||||
}
|
||||
|
||||
func main() {
|
||||
handler := loggingMiddleware(process)
|
||||
handler("测试数据")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.3 defer 机制
|
||||
|
||||
### 4.3.1 基本用法
|
||||
|
||||
```go
|
||||
func deferExample() {
|
||||
fmt.Println("开始")
|
||||
defer fmt.Println("延迟 1")
|
||||
defer fmt.Println("延迟 2")
|
||||
fmt.Println("结束")
|
||||
}
|
||||
// 输出:
|
||||
// 开始
|
||||
// 结束
|
||||
// 延迟 2
|
||||
// 延迟 1
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `defer` 语句**立即执行**,但函数调用**延迟到外层函数返回前**
|
||||
- 多个 `defer` 按**后进先出(LIFO)** 顺序执行
|
||||
- 常用于资源清理(文件关闭、锁释放、数据库连接)
|
||||
|
||||
### 4.3.2 defer 参数求值时机
|
||||
|
||||
```go
|
||||
func deferArgs() {
|
||||
i := 0
|
||||
defer fmt.Println("i =", i) // i 在 defer 时求值
|
||||
i = 10
|
||||
// 输出:i = 0
|
||||
}
|
||||
|
||||
func deferArgs2() {
|
||||
i := 0
|
||||
defer func() {
|
||||
fmt.Println("i =", i) // i 在函数执行时求值
|
||||
}()
|
||||
i = 10
|
||||
// 输出:i = 10
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 普通函数调用的参数在 `defer` 时**立即求值**
|
||||
- 闭包的变量在闭包**执行时求值**
|
||||
|
||||
### 4.3.3 defer 的返回值修改
|
||||
|
||||
```go
|
||||
func deferReturn() (result int) {
|
||||
defer func() {
|
||||
result += 10 // 修改命名返回值
|
||||
}()
|
||||
result = 5
|
||||
return // 返回 15
|
||||
}
|
||||
|
||||
func deferReturn2() int {
|
||||
defer func() {
|
||||
// 无法修改返回值(无名)
|
||||
}()
|
||||
return 5
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3.4 常见陷阱
|
||||
|
||||
#### 陷阱 1:在循环中使用 defer
|
||||
|
||||
```go
|
||||
func deferLoopBug() {
|
||||
for i := 0; i < 10; i++ {
|
||||
f, _ := os.Open("file.txt")
|
||||
defer f.Close() // 所有 defer 在函数结束时执行
|
||||
// 文件描述符可能耗尽
|
||||
}
|
||||
}
|
||||
|
||||
// 正确做法
|
||||
func deferLoopFix() {
|
||||
for i := 0; i < 10; i++ {
|
||||
func() {
|
||||
f, _ := os.Open("file.txt")
|
||||
defer f.Close()
|
||||
// 使用 f
|
||||
}() // 立即返回,defer 执行
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 陷阱 2:defer 掩盖错误
|
||||
|
||||
```go
|
||||
func deferError() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
panic("出错了")
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.4 panic 与 recover
|
||||
|
||||
### 4.4.1 panic 基础
|
||||
|
||||
```go
|
||||
func panicExample() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("捕获 panic:", r)
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Println("开始")
|
||||
panic("发生错误")
|
||||
fmt.Println("不会执行")
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- `panic` 立即停止当前函数,开始**栈展开**
|
||||
- `recover` 必须在 `defer` 中调用才能捕获 panic
|
||||
- panic 会**终止程序**,除非被 recover 捕获
|
||||
|
||||
### 4.4.2 使用场景
|
||||
|
||||
#### 1. 不可恢复的错误
|
||||
|
||||
```go
|
||||
func divide(a, b int) int {
|
||||
if b == 0 {
|
||||
panic("除数不能为零") // 编程错误,应该避免
|
||||
}
|
||||
return a / b
|
||||
}
|
||||
|
||||
// 正确做法:返回错误
|
||||
func divideSafe(a, b int) (int, error) {
|
||||
if b == 0 {
|
||||
return 0, fmt.Errorf("除数不能为零")
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 初始化失败
|
||||
|
||||
```go
|
||||
var config *Config
|
||||
|
||||
func init() {
|
||||
c, err := loadConfig()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("加载配置失败:%v", err))
|
||||
}
|
||||
config = c
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4.3 自定义 panic 类型
|
||||
|
||||
```go
|
||||
type MyError struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e MyError) Error() string {
|
||||
return fmt.Sprintf("错误码 %d: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
func safeDivide(a, b int) (int, error) {
|
||||
if b == 0 {
|
||||
panic(MyError{Code: 1001, Message: "除数不能为零"})
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
|
||||
func handlePanic() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if err, ok := r.(MyError); ok {
|
||||
fmt.Printf("自定义错误:%v\n", err)
|
||||
} else {
|
||||
fmt.Printf("未知 panic: %v\n", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
safeDivide(10, 0)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.5 接口(Interfaces)
|
||||
|
||||
### 4.5.1 接口定义与实现
|
||||
|
||||
```go
|
||||
type Speaker interface {
|
||||
Speak() string
|
||||
}
|
||||
|
||||
type Dog struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (d Dog) Speak() string {
|
||||
return d.Name + " 汪汪叫"
|
||||
}
|
||||
|
||||
type Cat struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c Cat) Speak() string {
|
||||
return c.Name + " 喵喵叫"
|
||||
}
|
||||
|
||||
func makeSound(s Speaker) {
|
||||
fmt.Println(s.Speak())
|
||||
}
|
||||
|
||||
func main() {
|
||||
dog := Dog{Name: "旺财"}
|
||||
cat := Cat{Name: "咪咪"}
|
||||
|
||||
makeSound(dog) // 旺财 汪汪叫
|
||||
makeSound(cat) // 咪咪 喵喵叫
|
||||
}
|
||||
```
|
||||
|
||||
**深度解析**:
|
||||
- 接口是**隐式实现**:不需要 `implements` 关键字
|
||||
- 只要类型实现了接口的所有方法,就**自动实现**该接口
|
||||
- 接口变量可以存储**任何实现该接口的类型**
|
||||
|
||||
### 4.5.2 空接口
|
||||
|
||||
```go
|
||||
func printAny(v interface{}) {
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// Go 1.18+ 可以使用 any(interface{}的别名)
|
||||
func printAny2(v any) {
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
func main() {
|
||||
printAny(1)
|
||||
printAny("hello")
|
||||
printAny(3.14)
|
||||
printAny([]int{1, 2, 3})
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.3 类型断言
|
||||
|
||||
```go
|
||||
func typeAssertion() {
|
||||
var v interface{} = "hello"
|
||||
|
||||
// 基本断言
|
||||
s := v.(string)
|
||||
fmt.Println(s)
|
||||
|
||||
// 安全断言
|
||||
if n, ok := v.(int); ok {
|
||||
fmt.Println(n)
|
||||
} else {
|
||||
fmt.Println("不是 int 类型")
|
||||
}
|
||||
|
||||
// 类型开关
|
||||
switch t := v.(type) {
|
||||
case int:
|
||||
fmt.Printf("整数:%d\n", t)
|
||||
case string:
|
||||
fmt.Printf("字符串:%s\n", t)
|
||||
case float64:
|
||||
fmt.Printf("浮点数:%.2f\n", t)
|
||||
default:
|
||||
fmt.Printf("未知类型:%T\n", t)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.4 接口的底层实现
|
||||
|
||||
**深度解析**:
|
||||
Go 的接口在底层由两个字段组成:
|
||||
- `data`:指向实际数据的指针
|
||||
- `type`:类型的描述信息(`_type`)
|
||||
|
||||
```go
|
||||
// 接口底层结构(伪代码)
|
||||
type iface struct {
|
||||
tab *itab // 接口表:包含类型和方法
|
||||
data unsafe.Pointer
|
||||
}
|
||||
|
||||
type itab struct {
|
||||
inter *interfacetype // 接口类型
|
||||
_type *_type // 具体类型
|
||||
hash uint32 // 类型哈希
|
||||
fun [1]unsafe.Pointer // 方法表
|
||||
}
|
||||
```
|
||||
|
||||
**空接口**(`interface{}`):
|
||||
- 所有类型都实现空接口
|
||||
- 底层是 `eface` 结构
|
||||
|
||||
```go
|
||||
type eface struct {
|
||||
_type *_type
|
||||
data unsafe.Pointer
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.5 接口最佳实践
|
||||
|
||||
#### 1. 小接口优于大接口
|
||||
|
||||
```go
|
||||
// 错误:接口太大
|
||||
type BigInterface interface {
|
||||
Read() []byte
|
||||
Write([]byte) (int, error)
|
||||
Close() error
|
||||
Seek(int64, int) (int64, error)
|
||||
// ... 很多方法
|
||||
}
|
||||
|
||||
// 正确:拆分为小接口
|
||||
type Reader interface {
|
||||
Read() []byte
|
||||
}
|
||||
|
||||
type Writer interface {
|
||||
Write([]byte) (int, error)
|
||||
}
|
||||
|
||||
type Closer interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// 组合接口
|
||||
type ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 接口定义在使用者一侧
|
||||
|
||||
```go
|
||||
// 错误:在定义者一侧定义接口
|
||||
type Dog struct{}
|
||||
func (d Dog) Speak() string { return "汪汪" }
|
||||
|
||||
// 在别处定义接口
|
||||
type Speaker interface {
|
||||
Speak() string
|
||||
}
|
||||
|
||||
// 正确:在使用者一侧定义
|
||||
func process(s Speaker) {
|
||||
fmt.Println(s.Speak())
|
||||
}
|
||||
|
||||
// Dog 自动实现 Speaker
|
||||
```
|
||||
|
||||
#### 3. 避免过度抽象
|
||||
|
||||
```go
|
||||
// 错误:不必要的接口
|
||||
type Logger interface {
|
||||
Log(string)
|
||||
}
|
||||
|
||||
type FileLogger struct{}
|
||||
func (f FileLogger) Log(msg string) {
|
||||
fmt.Println("File:", msg)
|
||||
}
|
||||
|
||||
func useLogger(l Logger) {
|
||||
l.Log("test")
|
||||
}
|
||||
|
||||
// 正确:直接使用具体类型(如果不需要多态)
|
||||
func useLogger2(l FileLogger) {
|
||||
l.Log("test")
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.6 深度实践:综合案例
|
||||
|
||||
### 4.6.1 插件系统
|
||||
|
||||
```go
|
||||
type Plugin interface {
|
||||
Name() string
|
||||
Run() error
|
||||
}
|
||||
|
||||
type HelloPlugin struct{}
|
||||
|
||||
func (h HelloPlugin) Name() string {
|
||||
return "HelloPlugin"
|
||||
}
|
||||
|
||||
func (h HelloPlugin) Run() error {
|
||||
fmt.Println("Hello from plugin!")
|
||||
return nil
|
||||
}
|
||||
|
||||
type MathPlugin struct{}
|
||||
|
||||
func (m MathPlugin) Name() string {
|
||||
return "MathPlugin"
|
||||
}
|
||||
|
||||
func (m MathPlugin) Run() error {
|
||||
fmt.Printf("2 + 2 = %d\n", 2+2)
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadPlugins() []Plugin {
|
||||
return []Plugin{
|
||||
HelloPlugin{},
|
||||
MathPlugin{},
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
plugins := loadPlugins()
|
||||
for _, p := range plugins {
|
||||
fmt.Printf("加载插件:%s\n", p.Name())
|
||||
if err := p.Run(); err != nil {
|
||||
fmt.Println("插件运行失败:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6.2 中间件链
|
||||
|
||||
```go
|
||||
type Middleware func(Handler) Handler
|
||||
type Handler func(string) string
|
||||
|
||||
func logging(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
fmt.Printf("[LOG] 输入:%s\n", input)
|
||||
result := next(input)
|
||||
fmt.Printf("[LOG] 输出:%s\n", result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func timing(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
start := time.Now()
|
||||
result := next(input)
|
||||
fmt.Printf("[TIME] 耗时:%v\n", time.Since(start))
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func auth(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
if input == "" {
|
||||
return "错误:空输入"
|
||||
}
|
||||
return next(input)
|
||||
}
|
||||
}
|
||||
|
||||
func businessLogic(input string) string {
|
||||
return "处理结果:" + input
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 构建中间件链
|
||||
handler := auth(timing(logging(businessLogic)))
|
||||
|
||||
result := handler("测试数据")
|
||||
fmt.Println("最终结果:", result)
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6.3 错误处理链
|
||||
|
||||
```go
|
||||
type AppError struct {
|
||||
Code int
|
||||
Message string
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *AppError) Error() string {
|
||||
if e.Cause != nil {
|
||||
return fmt.Sprintf("错误码 %d: %s (原因:%v)", e.Code, e.Message, e.Cause)
|
||||
}
|
||||
return fmt.Sprintf("错误码 %d: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
func wrapError(err error, code int, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &AppError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
func readFile(path string) ([]byte, error) {
|
||||
// 模拟读取失败
|
||||
return nil, fmt.Errorf("文件不存在")
|
||||
}
|
||||
|
||||
func processFile(path string) error {
|
||||
data, err := readFile(path)
|
||||
if err != nil {
|
||||
return wrapError(err, 1001, "读取文件失败")
|
||||
}
|
||||
|
||||
// 处理数据
|
||||
if len(data) == 0 {
|
||||
return wrapError(nil, 1002, "文件为空")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := processFile("test.txt")
|
||||
if err != nil {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
fmt.Printf("应用错误:代码=%d, 消息=%s\n", appErr.Code, appErr.Message)
|
||||
if appErr.Cause != nil {
|
||||
fmt.Printf("根本原因:%v\n", appErr.Cause)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("未知错误:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.7 常见陷阱与最佳实践
|
||||
|
||||
### 4.7.1 闭包陷阱总结
|
||||
|
||||
1. **循环变量捕获**:循环中使用闭包时,变量会共享
|
||||
2. **内存泄漏**:闭包捕获大对象导致无法回收
|
||||
3. **延迟求值**:注意 defer 中参数的求值时机
|
||||
|
||||
### 4.7.2 defer 陷阱总结
|
||||
|
||||
1. **循环中 defer**:可能导致资源耗尽
|
||||
2. **返回值修改**:只能修改命名返回值
|
||||
3. **掩盖错误**:recover 可能掩盖真正的 bug
|
||||
|
||||
### 4.7.3 接口陷阱总结
|
||||
|
||||
1. **空接口滥用**:过度使用 `interface{}` 失去类型安全
|
||||
2. **接口过大**:定义太多方法的接口难以实现
|
||||
3. **值 vs 指针**:值类型实现接口,指针类型不实现(反之亦然)
|
||||
|
||||
```go
|
||||
type S struct{}
|
||||
func (s S) Method() {}
|
||||
|
||||
var _ interface{ Method() } = S{} // 正确
|
||||
var _ interface{ Method() } = &S{} // 正确
|
||||
// var _ interface{ Method() } = (*S)(nil) // 错误:nil 指针不实现
|
||||
```
|
||||
|
||||
### 4.7.4 最佳实践
|
||||
|
||||
1. **优先使用错误返回**:panic 仅用于不可恢复错误
|
||||
2. **小接口**:定义最小必要的接口方法
|
||||
3. **闭包谨慎**:避免捕获大对象,循环中注意变量作用域
|
||||
4. **defer 适度**:仅在需要清理资源时使用
|
||||
5. **类型断言安全**:使用 `ok` 模式避免 panic
|
||||
|
||||
---
|
||||
|
||||
## 4.8 课后练习
|
||||
|
||||
1. **闭包计数器**:实现一个支持 `increment()`、`decrement()`、`reset()` 的计数器
|
||||
2. **中间件框架**:实现一个简单的 HTTP 中间件框架
|
||||
3. **错误包装**:实现一个支持错误链包装的工具函数
|
||||
4. **接口组合**:设计一组小接口,组合成复杂功能
|
||||
5. **defer 测试**:编写测试验证 defer 的执行顺序和参数求值时机
|
||||
|
||||
## 4.9 下一步
|
||||
|
||||
完成本章后,你将进入第五章:**并发编程**,深入学习 Go 最强大的特性——Goroutine 和 Channel,以及并发模式、同步原语和并发最佳实践。
|
||||
|
||||
---
|
||||
|
||||
**代码仓库位置**:https://giter.top/openclaw/test/tree/main/chapters/chapter-4
|
||||
|
||||
**下一章预告**:Goroutine 原理、Channel 通信模式、sync 包、并发设计模式、竞态检测
|
||||
3
chapters/chapter-4/go.mod
Normal file
3
chapters/chapter-4/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-tutorial
|
||||
|
||||
go 1.21
|
||||
429
chapters/chapter-4/main.go
Normal file
429
chapters/chapter-4/main.go
Normal file
@@ -0,0 +1,429 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 4.1 函数基础
|
||||
func add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
func divide(a, b float64) (float64, error) {
|
||||
if b == 0 {
|
||||
return 0, fmt.Errorf("除数不能为零")
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
|
||||
func divideNamed(a, b float64) (result float64, err error) {
|
||||
if b == 0 {
|
||||
err = fmt.Errorf("除数不能为零")
|
||||
return
|
||||
}
|
||||
result = a / b
|
||||
return
|
||||
}
|
||||
|
||||
func sum(nums ...int) int {
|
||||
total := 0
|
||||
for _, n := range nums {
|
||||
total += n
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func functionBasics() {
|
||||
fmt.Println("=== 函数基础 ===")
|
||||
fmt.Println("add(3, 4):", add(3, 4))
|
||||
|
||||
result, err := divide(10, 2)
|
||||
if err != nil {
|
||||
fmt.Println("错误:", err)
|
||||
} else {
|
||||
fmt.Printf("10 / 2 = %.2f\n", result)
|
||||
}
|
||||
|
||||
result2, _ := divideNamed(20, 4)
|
||||
fmt.Printf("20 / 4 = %.2f\n", result2)
|
||||
|
||||
fmt.Println("sum(1,2,3,4,5):", sum(1, 2, 3, 4, 5))
|
||||
nums := []int{10, 20, 30}
|
||||
fmt.Println("sum(nums...):", sum(nums...))
|
||||
}
|
||||
|
||||
// 4.2 闭包
|
||||
func createCounter() func() int {
|
||||
count := 0
|
||||
return func() int {
|
||||
count++
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
func createMultiplier(factor int) func(int) int {
|
||||
return func(x int) int {
|
||||
return x * factor
|
||||
}
|
||||
}
|
||||
|
||||
func closureLoopBug() {
|
||||
funcs := make([]func(), 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
funcs[i] = func() {
|
||||
fmt.Print(i, " ")
|
||||
}
|
||||
}
|
||||
fmt.Print("Bug: ")
|
||||
for _, f := range funcs {
|
||||
f()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func closureLoopFix() {
|
||||
funcs := make([]func(), 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
i := i
|
||||
funcs[i] = func() {
|
||||
fmt.Print(i, " ")
|
||||
}
|
||||
}
|
||||
fmt.Print("Fix: ")
|
||||
for _, f := range funcs {
|
||||
f()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func closureDemo() {
|
||||
fmt.Println("\n=== 闭包示例 ===")
|
||||
|
||||
counter := createCounter()
|
||||
fmt.Println("counter():", counter()) // 1
|
||||
fmt.Println("counter():", counter()) // 2
|
||||
fmt.Println("counter():", counter()) // 3
|
||||
|
||||
double := createMultiplier(2)
|
||||
triple := createMultiplier(3)
|
||||
fmt.Println("double(5):", double(5)) // 10
|
||||
fmt.Println("triple(5):", triple(5)) // 15
|
||||
|
||||
closureLoopBug()
|
||||
closureLoopFix()
|
||||
}
|
||||
|
||||
// 4.3 defer
|
||||
func deferDemo() {
|
||||
fmt.Println("\n=== defer 示例 ===")
|
||||
|
||||
defer fmt.Println("延迟 1")
|
||||
defer fmt.Println("延迟 2")
|
||||
fmt.Println("立即执行")
|
||||
|
||||
// defer 参数求值
|
||||
i := 0
|
||||
defer fmt.Println("defer 时求值 i =", i)
|
||||
i = 10
|
||||
|
||||
// 闭包 defer
|
||||
defer func() {
|
||||
fmt.Println("执行时求值 i =", i)
|
||||
}()
|
||||
|
||||
fmt.Println("defer 结束")
|
||||
}
|
||||
|
||||
func deferLoopDemo() {
|
||||
fmt.Println("\n=== defer 循环陷阱 ===")
|
||||
|
||||
// 错误:循环中 defer
|
||||
for i := 0; i < 3; i++ {
|
||||
f, err := os.Create(fmt.Sprintf("temp%d.txt", i))
|
||||
if err != nil {
|
||||
fmt.Println("创建文件失败:", err)
|
||||
continue
|
||||
}
|
||||
defer f.Close() // 所有 defer 在函数结束时执行
|
||||
f.WriteString("test")
|
||||
}
|
||||
|
||||
// 正确:立即执行
|
||||
for i := 0; i < 3; i++ {
|
||||
func() {
|
||||
f, err := os.Create(fmt.Sprintf("temp2_%d.txt", i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
f.WriteString("test")
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Println("文件操作完成")
|
||||
}
|
||||
|
||||
// 4.4 panic 和 recover
|
||||
func panicDemo() {
|
||||
fmt.Println("\n=== panic 和 recover ===")
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("捕获 panic:", r)
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Println("开始")
|
||||
panic("发生错误")
|
||||
fmt.Println("不会执行")
|
||||
}
|
||||
|
||||
type MyError struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e MyError) Error() string {
|
||||
return fmt.Sprintf("错误码 %d: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
func safeDivide(a, b int) int {
|
||||
if b == 0 {
|
||||
panic(MyError{Code: 1001, Message: "除数不能为零"})
|
||||
}
|
||||
return a / b
|
||||
}
|
||||
|
||||
func handlePanic() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if err, ok := r.(MyError); ok {
|
||||
fmt.Printf("自定义错误:代码=%d, 消息=%s\n", err.Code, err.Message)
|
||||
} else {
|
||||
fmt.Printf("未知 panic: %v\n", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
safeDivide(10, 0)
|
||||
}
|
||||
|
||||
func panicRecoverDemo() {
|
||||
panicDemo()
|
||||
handlePanic()
|
||||
}
|
||||
|
||||
// 4.5 接口
|
||||
type Speaker interface {
|
||||
Speak() string
|
||||
}
|
||||
|
||||
type Dog struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (d Dog) Speak() string {
|
||||
return d.Name + " 汪汪叫"
|
||||
}
|
||||
|
||||
type Cat struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c Cat) Speak() string {
|
||||
return c.Name + " 喵喵叫"
|
||||
}
|
||||
|
||||
func makeSound(s Speaker) {
|
||||
fmt.Println(s.Speak())
|
||||
}
|
||||
|
||||
func typeAssertionDemo() {
|
||||
fmt.Println("\n=== 类型断言 ===")
|
||||
|
||||
var v interface{} = "hello"
|
||||
|
||||
// 基本断言
|
||||
if s, ok := v.(string); ok {
|
||||
fmt.Println("是字符串:", s)
|
||||
}
|
||||
|
||||
// 类型开关
|
||||
switch t := v.(type) {
|
||||
case int:
|
||||
fmt.Printf("整数:%d\n", t)
|
||||
case string:
|
||||
fmt.Printf("字符串:%s\n", t)
|
||||
default:
|
||||
fmt.Printf("未知类型:%T\n", t)
|
||||
}
|
||||
}
|
||||
|
||||
func interfaceDemo() {
|
||||
dog := Dog{Name: "旺财"}
|
||||
cat := Cat{Name: "咪咪"}
|
||||
|
||||
makeSound(dog)
|
||||
makeSound(cat)
|
||||
|
||||
typeAssertionDemo()
|
||||
}
|
||||
|
||||
// 4.6 综合案例:插件系统
|
||||
type Plugin interface {
|
||||
Name() string
|
||||
Run() error
|
||||
}
|
||||
|
||||
type HelloPlugin struct{}
|
||||
|
||||
func (h HelloPlugin) Name() string {
|
||||
return "HelloPlugin"
|
||||
}
|
||||
|
||||
func (h HelloPlugin) Run() error {
|
||||
fmt.Println("Hello from plugin!")
|
||||
return nil
|
||||
}
|
||||
|
||||
type MathPlugin struct{}
|
||||
|
||||
func (m MathPlugin) Name() string {
|
||||
return "MathPlugin"
|
||||
}
|
||||
|
||||
func (m MathPlugin) Run() error {
|
||||
fmt.Printf("2 + 2 = %d\n", 2+2)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginDemo() {
|
||||
fmt.Println("\n=== 插件系统 ===")
|
||||
|
||||
plugins := []Plugin{
|
||||
HelloPlugin{},
|
||||
MathPlugin{},
|
||||
}
|
||||
|
||||
for _, p := range plugins {
|
||||
fmt.Printf("加载插件:%s\n", p.Name())
|
||||
if err := p.Run(); err != nil {
|
||||
fmt.Println("插件运行失败:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.6 综合案例:中间件链
|
||||
type Middleware func(Handler) Handler
|
||||
type Handler func(string) string
|
||||
|
||||
func logging(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
fmt.Printf("[LOG] 输入:%s\n", input)
|
||||
result := next(input)
|
||||
fmt.Printf("[LOG] 输出:%s\n", result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func timing(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
start := time.Now()
|
||||
result := next(input)
|
||||
fmt.Printf("[TIME] 耗时:%v\n", time.Since(start))
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func auth(next Handler) Handler {
|
||||
return func(input string) string {
|
||||
if input == "" {
|
||||
return "错误:空输入"
|
||||
}
|
||||
return next(input)
|
||||
}
|
||||
}
|
||||
|
||||
func businessLogic(input string) string {
|
||||
return "处理结果:" + input
|
||||
}
|
||||
|
||||
func middlewareDemo() {
|
||||
fmt.Println("\n=== 中间件链 ===")
|
||||
|
||||
handler := auth(timing(logging(businessLogic)))
|
||||
result := handler("测试数据")
|
||||
fmt.Println("最终结果:", result)
|
||||
}
|
||||
|
||||
// 4.6 综合案例:错误包装
|
||||
type AppError struct {
|
||||
Code int
|
||||
Message string
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *AppError) Error() string {
|
||||
if e.Cause != nil {
|
||||
return fmt.Sprintf("错误码 %d: %s (原因:%v)", e.Code, e.Message, e.Cause)
|
||||
}
|
||||
return fmt.Sprintf("错误码 %d: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
func wrapError(err error, code int, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &AppError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
func readFile(path string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("文件不存在")
|
||||
}
|
||||
|
||||
func processFile(path string) error {
|
||||
data, err := readFile(path)
|
||||
if err != nil {
|
||||
return wrapError(err, 1001, "读取文件失败")
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return wrapError(nil, 1002, "文件为空")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func errorHandlingDemo() {
|
||||
fmt.Println("\n=== 错误处理链 ===")
|
||||
|
||||
err := processFile("test.txt")
|
||||
if err != nil {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
fmt.Printf("应用错误:代码=%d, 消息=%s\n", appErr.Code, appErr.Message)
|
||||
if appErr.Cause != nil {
|
||||
fmt.Printf("根本原因:%v\n", appErr.Cause)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("未知错误:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
functionBasics()
|
||||
closureDemo()
|
||||
deferDemo()
|
||||
deferLoopDemo()
|
||||
panicRecoverDemo()
|
||||
interfaceDemo()
|
||||
pluginDemo()
|
||||
middlewareDemo()
|
||||
errorHandlingDemo()
|
||||
}
|
||||
Reference in New Issue
Block a user