在Go中创建自定义错误的方式总结

 更新时间:2024年01月16日 14:51:28   作者:CHQIUU  
在程序开发中错误处理是至关重要的,下面这篇文章主要给大家介绍了关于在Go中创建自定义错误的方式,文中通过代码介绍的非常详细,对大家学习或者使用Go具有一定的参考借鉴价值,需要的朋友可以参考下

引言

Go提供了两种在标准库中创建错误的方法,[errors.Newfmt.Errorf],当与用户交流更复杂的错误信息时,或在调试时与未来的自己交流时,有时这两种机制不足以充分捕获和报告所发生的情况。为了传达更复杂的错误信息并实现更多的功能,我们可以实现标准库接口类型,error

其语法如下:

type error interface {
  Error() string
}

builtin包将error定义为一个接口,它只有一个Error()方法,返回一个字符串形式的错误消息。通过实现这个方法,我们可以将定义的任何类型转换为我们自己的error。

让我们尝试运行以下示例来查看error接口的实现:

package main

import (
	"fmt"
	"os"
)

type MyError struct{}

func (m *MyError) Error() string {
	return "boom"
}

func sayHello() (string, error) {
	return "", &MyError{}
}

func main() {
	s, err := sayHello()
	if err != nil {
		fmt.Println("unexpected error: err:", err)
		os.Exit(1)
	}
	fmt.Println("The string:", s)
}
Outputunexpected error: err: boom
exit status 1

在这里,我们创建了一个新的空结构类型MyError,并在其上定义了Error()方法。Error()方法返回字符串"boom"

main()中,我们调用函数sayHello,该函数返回一个空字符串和一个新的MyError实例。由于sayHello总是会返回错误,所以main()中的if语句体中的fmt.Println调用总是会执行。然后,我们使用fmt.Println打印短前缀字符串"unexpected error:"以及err变量中保存的MyError实例。

请注意,我们不必直接调用Error(),因为fmt包能够自动检测这是Error 的实现。它透明地调用Error()来获取字符串"boom",并将其与前缀字符串"unexpected Error: err:"连接起来。

在自定义错误中收集详细信息

有时候,自定义错误是捕获详细错误信息的最简洁方式。例如,假设我们想要捕获HTTP请求产生的错误的状态码;运行以下程序来查看error的实现,它允许我们清晰地捕获信息:

package main

import (
	"errors"
	"fmt"
	"os"
)

type RequestError struct {
	StatusCode int

	Err error
}

func (r *RequestError) Error() string {
	return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
}

func doRequest() error {
	return &RequestError{
		StatusCode: 503,
		Err:        errors.New("unavailable"),
	}
}

func main() {
	err := doRequest()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Println("success!")
}

Outputstatus 503: err unavailable
exit status 1

在这个例子中,我们创建了一个新的RequestError实例,并使用标准库中的errors.New函数提供状态码和一个错误。然后像前面的例子一样,我们使用fmt.Println打印它。

RequestErrorError()方法中,我们使用fmt.Sprintf函数来使用创建错误时提供的信息来构造一个字符串。

类型断言和自定义错误

error接口只公开了一个方法,但我们可能需要访问error实现的其他方法来正确处理错误。例如,我们可能有几个临时的error自定义实现,可以通过存在的 temporary()方法进行检索。

接口为类型提供了更广泛的方法集合,因此我们必须使用类型断言来更改view正在显示的方法,或者完全删除它。

下面的例子在之前的RequestError的基础上增加了一个Temporary()方法,该方法将表明调用者是否应该重试请求:

package main

import (
	"errors"
	"fmt"
	"net/http"
	"os"
)

type RequestError struct {
	StatusCode int

	Err error
}

func (r *RequestError) Error() string {
	return r.Err.Error()
}

func (r *RequestError) Temporary() bool {
	return r.StatusCode == http.StatusServiceUnavailable // 503
}

func doRequest() error {
	return &RequestError{
		StatusCode: 503,
		Err:        errors.New("unavailable"),
	}
}

func main() {
	err := doRequest()
	if err != nil {
		fmt.Println(err)
		re, ok := err.(*RequestError)
		if ok {
			if re.Temporary() {
				fmt.Println("This request can be tried again")
			} else {
				fmt.Println("This request cannot be tried again")
			}
		}
		os.Exit(1)
	}

	fmt.Println("success!")
}

Outputunavailable
This request can be tried again
exit status 1

main()中,我们调用doRequest(),它会返回一个error接口给我们。我们首先打印 error()方法返回的错误消息。接下来,我们尝试使用类型断言re, ok := err.(*RequestError)来暴露RequestError中的所有方法。如果类型断言成功,那么我们使用Temporary()方法来查看此错误是否为临时错误。由于doRequest()设置的StatusCode503,这与http.StatusServiceUnavailable匹配,因此返回true并导致打印" this request can be try again"。在实践中,我们会发送另一个请求,而不是打印一条消息。

包装错误

通常,错误会在程序之外产生,例如:数据库、网络连接等。这些错误提供的错误消息不能帮助任何人找到错误的根源。在错误消息的开头使用额外的信息包装错误,可以为成功调试提供一些必要的上下文。

下面的例子演示了我们如何将一些上下文信息附加到从其他函数返回的晦涩的error上:

package main

import (
	"errors"
	"fmt"
)

type WrappedError struct {
	Context string
	Err     error
}

func (w *WrappedError) Error() string {
	return fmt.Sprintf("%s: %v", w.Context, w.Err)
}

func Wrap(err error, info string) *WrappedError {
	return &WrappedError{
		Context: info,
		Err:     err,
	}
}

func main() {
	err := errors.New("boom!")
	err = Wrap(err, "main")

	fmt.Println(err)
}

Outputmain: boom!

WrappedError是一个有两个字段的结构体:一个是string类型的上下文消息,另一个是error类型,WrappedError提供了更多的信息。当Error()方法被调用时,我们再次使用fmt.Sprintf来打印上下文消息,然后Error(fmt.Sprintf也知道隐式调用Error()方法)。

main()中,我们使用errors.New创建一个错误,然后使用我们定义的wrap函数包装这个错误。这允许我们指出这个error是在"main"中生成的。此外,由于我们的WrappedError也是一个error,我们可以包装其他的WrappedError,这将允许我们看到一个链来帮助我们追踪错误的来源。在标准库的帮助下,我们甚至可以在错误中嵌入完整的堆栈跟踪。

总结

由于error接口只有一个方法,我们已经看到我们可以为不同的情况提供不同类型的错误。这可以涵盖从将多条信息作为错误的一部分进行沟通到实现指数回退的所有事情。虽然Go中的错误处理机制表面上看起来很简单,但我们可以使用这些自定义错误来处理常见和不常见的情况,从而实现相当丰富的处理。

Go还有另一种沟通意外行为的机制panics。在错误处理系列的下一篇文章中,我们将研究恐慌——它们是什么以及如何处理它们。

到此这篇关于在Go中创建自定义错误的文章就介绍到这了,更多相关Go创建自定义错误内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go并发4种方法简明讲解

    Go并发4种方法简明讲解

    这篇文章主要介绍了Go并发4种方法简明讲解,需要的朋友可以参考下
    2022-04-04
  • golang实现对JavaScript代码混淆

    golang实现对JavaScript代码混淆

    在Go语言中,你可以使用一些工具来混淆JavaScript代码,一个常用的工具是Terser,它可以用于压缩和混淆JavaScript代码,你可以通过Go语言的`os/exec`包来调用Terser工具,本文给通过一个简单的示例给大家介绍一下,感兴趣的朋友可以参考下
    2024-01-01
  • Go FX框架用法小结

    Go FX框架用法小结

    Go FX是一个功能强大的框架,通过依赖注入和生命周期管理来简化应用的构建与维护,本文就来介绍一下Go FX框架用法小结,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • Golang协程中channel和sync的角色扮演分析

    Golang协程中channel和sync的角色扮演分析

    文章介绍了Go语言中的channel和sync包的作用,详细说明了它们在协程通信、同步与协调以及避免共享状态方面的重要性,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • golang微服务框架kratos实现Socket.IO服务的方法

    golang微服务框架kratos实现Socket.IO服务的方法

    本文主要介绍了golang微服务框架kratos实现Socket.IO服务的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Golang拾遗之指针和接口的使用详解

    Golang拾遗之指针和接口的使用详解

    拾遗主要是收集和golang相关的琐碎知识,这篇文章主要是为大家整理了Golang中指针和接口的使用方法,文中的示例代码讲解详细,需要的可以参考一下
    2023-02-02
  • Go的固定时长定时器和周期性时长定时器

    Go的固定时长定时器和周期性时长定时器

    本文主要介绍了Go的固定时长定时器和周期性时长定时器,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • 浅谈golang通道类型

    浅谈golang通道类型

    本文主要介绍了浅谈golang通道类型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • golang 使用 viper 读取自定义配置文件

    golang 使用 viper 读取自定义配置文件

    这篇文章主要介绍了golang 使用 viper 读取自定义配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • 在Golang中实现RSA算法的加解密操作详解

    在Golang中实现RSA算法的加解密操作详解

    RSA 是一种非对称加密算法,广泛使用于数据的安全传输,crypto/rsa 是 Golang 中实现了 RSA 算法的一个标准库,提供了生成公私钥对、加解密数据、签名和验签等功能,本文给大家介绍了在Golang中实现RSA算法的加解密操作,需要的朋友可以参考下
    2023-12-12

最新评论