ZetCode

Golang panic 函数

最后修改时间 2025 年 5 月 8 日

本教程将讲解如何在 Go 中使用内置的 panic 函数。我们将通过实际的错误处理和恢复示例来介绍 panic 的基本用法。

panic 函数用于停止 Go 中的正常执行。当调用 panic 时,程序将开始展开堆栈,并执行延迟的函数。

在 Go 中,panic 通常用于不可恢复的错误。它应该仅用于程序无法继续执行的异常情况。

基本 panic 示例

panic 最简单的用法是立即停止程序执行。此示例演示了 panic 的基本行为。
注意: 在生产代码中应谨慎使用 panic。

basic_panic.go
package main

import "fmt"

func main() {
    fmt.Println("Starting program")
    
    panic("Something went terribly wrong!")
    
    fmt.Println("This line will never execute")
}

程序打印第一条消息然后 panic。由于执行在 panic 调用处停止,第二条消息永远不会被打印。

带 recover 的 panic

我们可以使用延迟调用中的 recover 函数来从 panic 中恢复。此示例展示了 panic 恢复的实际应用。

panic_recover.go
package main

import "fmt"

func mayPanic() {
    panic("a problem occurred")
}

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    mayPanic()
    
    fmt.Println("After mayPanic()")
}

延迟函数调用 recover 来捕获 panic。程序在从 panic 恢复后继续执行。

goroutine 中的 panic

goroutine 中的 panic 与主程序中的 panic 行为不同。此示例展示了并发代码中的 panic 处理。

goroutine_panic.go
package main

import (
    "fmt"
    "time"
)

func worker() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Worker recovered:", r)
        }
    }()
    
    panic("Worker failed")
}

func main() {
    go worker()
    
    time.Sleep(1 * time.Second)
    fmt.Println("Main function continues")
}

worker goroutine panic 了,但它自己进行了恢复。主 goroutine 继续执行,不受 worker panic 的影响。

带自定义类型的 panic

panic 可以接受任何值,包括自定义类型。此示例演示了如何将结构化数据与 panic 一起使用。

custom_panic.go
package main

import "fmt"

type ErrorDetails struct {
    Code    int
    Message string
}

func processInput(input int) {
    if input < 0 {
        panic(ErrorDetails{
            Code:    400,
            Message: "Negative input not allowed",
        })
    }
    fmt.Println("Processing input:", input)
}

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered:", r)
        }
    }()
    
    processInput(-5)
    fmt.Println("Program continues after recovery")
}

当检测到无效输入时,该函数将使用自定义 struct panic。恢复处理程序可以访问结构化的 panic 数据。

嵌套的 panic 和 recover

panic 和 recover 可以嵌套在函数调用中。此示例展示了 panic 如何通过多个函数调用进行复杂的传播。

nested_panic.go
package main

import "fmt"

func inner() {
    fmt.Println("Entering inner")
    panic("inner panic")
    fmt.Println("Leaving inner")
}

func middle() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Middle recovered:", r)
            panic("re-panicked in middle")
        }
    }()
    inner()
}

func outer() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Outer recovered:", r)
        }
    }()
    middle()
}

func main() {
    fmt.Println("Calling outer")
    outer()
    fmt.Println("Returned normally from outer")
}

panic 源于 inner,在 middle 中被捕获并重新 panic,最后在 outer 中被捕获。这表明 panic 如何沿着调用堆栈向上传播。

来源

Go 语言规范

本教程通过实际的错误处理和恢复模式,介绍了 Go 中的 panic 函数。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面有十多年的经验。

列出所有 Golang 教程