Go语言错误和异常实现

 更新时间:2025年10月19日 11:04:43   作者:数据知道  
本文介绍了Go语言的错误处理机制,包括通过返回error类型显式处理错误,以及使用panic和recover应对严重异常,感兴趣的可以了解一下

Go 语言没有像 Java 或 Python 那样的 try-catch 异常捕获机制,而是采用了一种显式的错误处理方式,即通过返回 error 类型来表示可能发生的错误。此外,Go 还提供了 panicrecover 机制用于处理程序运行时的严重错误。

一、Go 错误处理机制概述

在 Go 语言中,错误是可以预期的,并且不是非常严重,不会影响程序的运行。对于这类问题,可以用返回错误给调用者的方法,让调用者自己决定如何处理。

Go 语言推荐使用 多返回值 的方式返回错误,通常函数的最后一个返回值是 error 类型。调用者需要检查这个返回值是否为 nil,来判断是否发生了错误。

1.1 error 类型

在 Go 语言中,错误是通过内置的 error 接口表示的。它非常简单,只有一个 Error 方法用来返回具体的错误信息,定义如下:

type error interface {
    Error() string
}

任何实现了 Error() string 方法的类型都可以作为 error 使用。在下面的代码中,我演示了一个字符串转整数的例子:

func main() {
   i,err:=strconv.Atoi("a")
   if err!=nil {
      fmt.Println(err)
   }else {
      fmt.Println(i)
   }
}

这里我故意使用了字符串 “a”,尝试把它转为整数。我们知道 “a” 是无法转为数字的,所以运行这段程序,会打印出如下错误信息:

strconv.Atoi: parsing "a": invalid syntax

这个错误信息就是通过接口 error 返回的。我们来看关于函数 strconv.Atoi 的定义,如下所示:

func Atoi(s string) (int, error)

一般而言,error 接口用于当方法或者函数执行遇到错误时进行返回,而且是第二个返回值。通过这种方式,可以让调用者自己根据错误信息决定如何进行下一步处理。

1.2errors.New()和fmt.Errorf()

除了可以使用其他函数,自己定义的函数也可以返回错误信息给调用者。Go 提供了 errors.New()fmt.Errorf() 来创建错误。基本错误处理示例代码:

package main
import (
	"errors"
	"fmt"
)
func divide(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("division by zero")
	}
	return a / b, nil
}
func main() {
	result, err := divide(10, 0)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println("Result:", result)
}

输出:

Error: division by zero

Go 1.13 引入了错误包装(Error Wrapping)机制,可以使用 fmt.Errorf%w 动词将一个错误包装进另一个错误中,并保留原始错误信息。

错误包装示例代码:

package main
import (
	"errors"
	"fmt"
)
func openFile(filename string) error {
	return errors.New("file not found")
}
func readFile(filename string) error {
	err := openFile(filename)
	if err != nil {
		return fmt.Errorf("readFile failed: %w", err)
	}
	return nil
}
func main() {
	err := readFile("data.txt")
	if err != nil {
		fmt.Println("Error:", err)
		if errors.Is(err, errors.New("file not found")) {
			fmt.Println("It's a file not found error!")
		}
	}
}

输出:

Error: readFile failed: file not found
It's a file not found error!

1.3 自定义错误类型

我们可以通过实现 error 接口来定义自己的错误类型,这样可以携带更多上下文信息。自定义错误类型示例代码:

package main
import "fmt"
type MyError struct {
	Code    int
	Message string
}
func (e *MyError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
func checkAge(age int) error {
	if age < 18 {
		return &MyError{Code: 403, Message: "Access denied: underage"}
	}
	return nil
}
func main() {
	err := checkAge(16)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Access granted")
}

输出:

Error 403: Access denied: underage

二、panic 和 recover

Go 提供了 panicrecover 用于处理程序运行时的严重错误(如数组越界、空指针解引用等)。panic 会中断程序执行,而 recover 可以捕获 panic,避免程序崩溃。

2.1 panic 的使用

panic 用于表示程序遇到了无法继续执行的严重错误。panic示例代码:

package main
import "fmt"
func testPanic() {
	panic("Something went wrong!")
}
func main() {
	fmt.Println("Start")
	testPanic()
	fmt.Println("End") // 不会执行
}

输出:

Start
panic: Something went wrong!
goroutine 1 [running]:
main.testPanic()
        /tmp/sandbox123/prog.go:6 +0x39
main.main()
        /tmp/sandbox123/prog.go:10 +0x65

2.2 recover 的使用

recover 必须在 defer 中调用,用于捕获 panic,避免程序崩溃。recover示例代码:

package main
import "fmt"
func safeExecute() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered from panic:", r)
		}
	}()
	panic("Oops! Panic occurred")
}
func main() {
	fmt.Println("Start")
	safeExecute()
	fmt.Println("End")
}

输出:

Start
Recovered from panic: Oops! Panic occurred
End

三、Deferred 函数

在一个自定义函数中,你打开了一个文件,然后需要关闭它以释放资源。不管你的代码执行了多少分支,是否出现了错误,文件是一定要关闭的,这样才能保证资源的释放。

如果这个事情由开发人员来做,随着业务逻辑的复杂会变得非常麻烦,而且还有可能会忘记关闭。基于这种情况,Go 语言为我们提供了 defer 函数,可以保证文件关闭后一定会被执行,不管你自定义的函数出现异常还是错误。

下面的代码是 Go 语言标准包 ioutil 中的 ReadFile 函数,它需要打开一个文件,然后通过 defer 关键字确保在 ReadFile 函数执行结束后,f.Close() 方法被执行,这样文件的资源才一定会释放。

func ReadFile(filename string) ([]byte, error) {
   f, err := os.Open(filename)
   if err != nil {
      return nil, err
   }
   defer f.Close()
   //省略无关代码
   return readAll(f, n)
}

defer 关键字用于修饰一个函数或者方法,使得该函数或者方法在返回前才会执行,也就说被延迟,但又可以保证一定会执行。

以上面的 ReadFile 函数为例,被 defer 修饰的 f.Close 方法延迟执行,也就是说会先执行 readAll(f, n),然后在整个 ReadFile 函数 return 之前执行 f.Close 方法。

defer 语句常被用于成对的操作,如文件的打开和关闭,加锁和释放锁,连接的建立和断开等。不管多么复杂的操作,都可以保证资源被正确地释放。

四、使用建议

  1. 优先使用 error 返回值:Go 推荐使用显式的错误返回值,而不是 panic。
  2. 错误信息要清晰:错误信息应该包含足够的上下文,便于调试。
  3. 使用错误包装:使用 fmt.Errorf 和 %w 包装错误,保留原始错误信息。
  4. 谨慎使用 panic:panic 应该用于不可恢复的错误,如程序初始化失败。
  5. recover 只在必要时使用:recover 主要用于库或框架中,避免程序崩溃,一般业务代码不建议滥用。

到此这篇关于Go语言错误和异常实现的文章就介绍到这了,更多相关Go语言错误和异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用Gomock进行单元测试的方法示例

    使用Gomock进行单元测试的方法示例

    这篇文章主要介绍了使用Gomock进行单元测试的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • go语言Timer计时器的用法示例详解

    go语言Timer计时器的用法示例详解

    Go语言的标准库里提供两种类型的计时器Timer和Ticker。这篇文章通过实例代码给大家介绍go语言Timer计时器的用法,代码简单易懂,感兴趣的朋友跟随小编一起看看吧
    2020-05-05
  • GO语言中创建切片的三种实现方式

    GO语言中创建切片的三种实现方式

    这篇文章主要介绍了GO语言中创建切片的三种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Goland Gin 框架中的表单处理与数据绑定的操作方法

    Goland Gin 框架中的表单处理与数据绑定的操作方法

    本文详细介绍了Gin框架中表单处理的功能,包括数据绑定、验证和文件上传等,并通过一个完整的用户注册项目示例展示了实际应用,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • Go语言make创建切片的五种方式

    Go语言make创建切片的五种方式

    本文主要介绍了Go语言make创建切片的五种方式,包括指定长度、预分配容量、零长度空切片、带容量缓冲区、匿名结构体切片,具有一定的参考价值,感兴趣的可以了解一下
    2025-09-09
  • Go语言普通指针unsafe.Pointer uintpt之间的关系及指针运算

    Go语言普通指针unsafe.Pointer uintpt之间的关系及指针运算

    这篇文章主要为大家介绍了Go语言普通指针unsafe.Pointer uintpt之间的关系及指针运算示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Golang中的环境配置方式

    Golang中的环境配置方式

    这篇文章主要介绍了Golang中的环境配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • Golang的关键字defer的使用方法

    Golang的关键字defer的使用方法

    这篇文章主要介绍了Golang的关键字defer的使用方法,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-06-06
  • 详解Go语言如何利用上下文进行并发计算

    详解Go语言如何利用上下文进行并发计算

    在Go编程中,上下文(context)是一个非常重要的概念,它包含了与请求相关的信息,本文主要来和大家讨论一下如何在并发计算中使用上下文,感兴趣的可以了解下
    2024-02-02
  • Go语言制作svg格式树形图的示例代码

    Go语言制作svg格式树形图的示例代码

    SVG是可伸缩矢量图形 (Scalable Vector Graphics),于2003年1月14日成为 W3C 推荐标准。本文将利用Go语言实现制作svg格式树形图,感兴趣的可以了解一下
    2022-09-09

最新评论