go 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {                                                                            
ret := practice()
fmt.Println(ret)
}
func practice() (result int) {
defer func() {
if r := recover(); r != nil {
log.Println("Recovered in f", r)
// 断言, 参考第七章7.10节的内容
// see: https://zhuanlan.zhihu.com/p/149015320

// 具体作用为检查接口中是否包含int类型的值, 并返回该值
// panic接受一个类型为any的参数
// 该参数的any类型实际为一个空接口
// 参考Go安装目录 /src/builtin/builtin.go:95 :252
result = r.(int) // 断言 int
}
}()
panic(34)
}

📄 Go 语言 panic/recover 机制代码分析文档

整理自用户提供的代码分析内容,聚焦 panic(34) + defer recover() + 命名返回值的组合用法。


🔁 执行流程图解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
main()


practice() // 函数签名: func practice() (result int)

├─► 注册 defer 匿名函数(捕获恐慌)

├─► panic(34) 触发
│ │
│ └─► 函数立即停止执行,跳转至 defer

└─► 执行 defer 函数:

├─► r := recover() 捕获 panic 值 → r = 34 (any 类型)

├─► r != nil 成立,进入处理逻辑

├─► r.(int) 类型断言 → 取出 int 值 34

└─► 赋值给命名返回值: result = 34


practice() 返回 result = 34

main() 打印输出: 34

❓ 问题解析

问题 1:panic(34) 是什么意思?

panic 的函数签名:

1
func panic(v any)  // any 即 interface{}

含义:抛出一个 panic,携带的值为整数 34

panic 可接受任意类型

1
2
3
4
panic("出错了")           // string
panic(34) // int
panic(errors.New("err")) // error
panic(bailout{}) // 自定义 struct 类型

📌 关键点34 本身无特殊语义,仅作为演示值。核心在于:你 panic 什么类型,recover 就能原样拿回什么类型(包装在 any 中)。


问题 2:recover() 如何正确使用?

✅ 标准三步曲写法:

1
2
3
4
5
6
7
8
9
10
defer func() {
// ① 调用 recover() 捕获 panic 值
if r := recover(); r != nil {

// ② 处理:类型断言还原原始值
result = r.(int)

// ③ 通过命名返回值将结果带出函数
}
}()

🔑 三个关键知识点:

要点 说明 示例/注意
① recover() 返回 any 类型 panic(v any),recover 拿到的也是 any 包装的值 r 类型是 any,值是 34
r.(int) 是类型断言 从空接口中提取具体类型 若类型不匹配会再次 panic;安全写法:if val, ok := r.(int); ok { ... }
③ 命名返回值 + defer 修改 defer 中修改 result,函数返回时自动携带该值 实现”不写 return 却返回非零值”的技巧

💡 命名返回值(Named Return Value)在函数签名中声明:func practice() (result int),defer 中可直接读写 result 变量。


🧩 核心模式总结

1
2
3
4
5
6
7
8
9
┌─────────────────────────────────────┐
│ panic 抛值 │
│ ↓ │
│ defer 中 recover 接值 │
│ ↓ │
│ 类型断言还原原始类型 │
│ ↓ │
│ 通过命名返回值将结果带出函数 │
└─────────────────────────────────────┘

🎯 本质理解:

panic/recover 机制非常规地用作控制流,实现”跳过正常执行路径,但依然返回预期结果”的效果。

📌 代码亮点:

  • practice() 函数从未执行 return 语句
  • 却通过 panic + defer + 命名返回值 成功返回 34
  • 展示了 Go 语言中异常处理机制的灵活性(⚠️ 但生产环境请谨慎使用,panic 应用于真正异常场景)

⚠️ 最佳实践提醒

1
2
3
4
5
6
7
8
9
10
// ✅ 推荐:安全类型断言
if val, ok := r.(int); ok {
result = val
}

// ❌ 避免:直接断言,类型不匹配会二次 panic
result = r.(int) // 若 r 实际是 string,此处会崩溃

// ✅ 推荐:panic 仅用于真正不可恢复的错误
// 而非用作常规控制流(本例仅为教学演示)

📚 关联知识点:本例是练习 5.19 的延伸,展示了不写 return 语句却能返回非零值的 Go 语言特性,核心依赖:命名返回值 + defer 修改作用域变量