go 创建自定义包_在Go中创建自定义错误

news/2024/7/4 13:42:26

go 创建自定义包

介绍 (Introduction)

Go provides two methods to create errors in the standard library, errors.New and fmt.Errorf. When communicating more complicated error information to your users, or to your future self when debugging, sometimes these two mechanisms are not enough to adequately capture and report what has happened. To convey this more complex error information and attain more functionality, we can implement the standard library interface type, error.

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

The syntax for this would be as follows:

语法如下:

type error interface {
  Error() string
}

The builtin package defines error as an interface with a single Error() method that returns an error message as a string. By implementing this method, we can transform any type we define into an error of our own.

builtin程序包使用单个Error()方法将error定义为接口,该方法以字符串形式返回错误消息。 通过实现此方法,我们可以将定义的任何类型转换为我们自己的错误。

Let’s try running the following example to see an implementation of the error interface:

让我们尝试运行以下示例以查看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)
}

We’ll see the following output:

我们将看到以下输出:


   
Output
unexpected error: err: boom exit status 1

Here we’ve created a new empty struct type, MyError, and defined the Error() method on it. The Error() method returns the string "boom".

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

Within main(), we call the function sayHello that returns an empty string and a new instance of MyError. Since sayHello will always return an error, the fmt.Println invocation within the body of the if statement in main() will always execute. We then use fmt.Println to print the short prefix string "unexpected error:" along with the instance of MyError held within the err variable.

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

Notice that we don’t have to directly call Error(), since the fmt package is able to automatically detect that this is an implementation of error. It calls Error() transparently to get the string "boom" and concatenates it with the prefix string "unexpected error: err:".

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

在自定义错误中收集详细信息 (Collecting Detailed Information in a Custom Error)

Sometimes a custom error is the cleanest way to capture detailed error information. For example, let’s say we want to capture the status code for errors produced by an HTTP request; run the following program to see an implementation of error that allows us to cleanly capture that information:

有时,自定义错误是捕获详细错误信息的最干净的方法。 例如,假设我们要捕获HTTP请求产生的错误的状态代码。 运行以下程序,查看error的实现,该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!")
}

We will see the following output:

我们将看到以下输出:


   
Output
status 503: err unavailable exit status 1

In this example, we create a new instance of RequestError and provide the status code and an error using the errors.New function from the standard library. We then print this using fmt.Println as in previous examples.

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

Within the Error() method of RequestError, we use the fmt.Sprintf function to construct a string using the information provided when the error was created.

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

类型断言和自定义错误 (Type Assertions and Custom Errors)

The error interface exposes only one method, but we may need to access the other methods of error implementations to handle an error properly. For example, we may have several custom implementations of error that are temporary and can be retried—denoted by the presence of a Temporary() method.

error接口仅公开一种方法,但我们可能需要访问error实现的其他方法才能正确处理错误。 例如,我们可能有一些error自定义实现,这些实现是临时的并且可以重试-通过Temporary()方法的存在来表示。

Interfaces provide a narrow view into the wider set of methods provided by types, so we must use a type assertion to change the methods that view is displaying, or to remove it entirely.

接口提供了由类型提供的更广泛的方法集的狭窄视图,因此我们必须使用类型断言来更改视图显示的方法,或将其完全删除。

The following example augments the RequestError shown previously to have a Temporary() method which will indicate whether or not callers should retry the request:

下面的示例将前面显示的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!")
}

We will see the following output:

我们将看到以下输出:


   
Output
unavailable This request can be tried again exit status 1

Within main(), we call doRequest() which returns an error interface to us. We first print the error message returned by the Error() method. Next, we attempt to expose all methods from RequestError by using the type assertion re, ok := err.(*RequestError). If the type assertion succeeded, we then use the Temporary() method to see if this error is a temporary error. Since the StatusCode set by doRequest() is 503, which matches http.StatusServiceUnavailable, this returns true and causes "This request can be tried again" to be printed. In practice, we would instead make another request rather than printing a message.

main() ,我们调用doRequest() ,它向我们返回一个error接口。 我们首先打印由Error()方法返回的错误消息。 接下来,我们尝试通过使用类型断言re, ok := err.(*RequestError)公开来自RequestError的所有方法。 如果类型声明成功,则使用Temporary()方法查看此错误是否是临时错误。 由于doRequest()设置的StatusCode503 ,与http.StatusServiceUnavailable匹配,因此返回true并导致打印"This request can be tried again" 。 实际上,我们会提出另一个请求,而不是打印一条消息。

包装错误 (Wrapping Errors)

Commonly, an error will be generated from something outside of your program such as: a database, a network connection, etc. The error messages provided from these errors don’t help anyone find the origin of the error. Wrapping errors with extra information at the beginning of an error message would provide some needed context for successful debugging.

通常,错误将从程序外部的某些内容生成,例如:数据库,网络连接等。这些错误提供的错误消息无法帮助任何人找到错误的根源。 在错误消息的开头用额外的信息包装错误将为成功调试提供一些必要的上下文。

The following example demonstrates how we can attach some contextual information to an otherwise cryptic error returned from some other function:

以下示例演示了如何将一些上下文信息附加到其他函数返回的否则为隐秘的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)
}

We will see the following output:

我们将看到以下输出:


   
Output
main: boom!

WrappedError is a struct with two fields: a context message as a string, and an error that this WrappedError is providing more information about. When the Error() method is invoked, we again use fmt.Sprintf to print the context message, then the error (fmt.Sprintf knows to implicitly call the Error() method as well).

WrappedError是具有两个字段的结构:作为string的上下文消息,以及此WrappedError提供了更多信息的error 。 调用Error()方法时,我们再次使用fmt.Sprintf打印上下文消息,然后显示error消息( fmt.Sprintf知道也隐式调用Error()方法)。

Within main(), we create an error using errors.New, and then we wrap that error using the Wrap function we defined. This allows us to indicate that this error was generated in "main". Also, since our WrappedError is also an error, we could wrap other WrappedErrors—this would allow us to see a chain to help us track down the source of the error. With a little help from the standard library, we can even embed complete stack traces in our errors.

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

结论 (Conclusion)

Since the error interface is only a single method, we’ve seen that we have great flexibility in providing different types of errors for different situations. This can encompass everything from communicating multiple pieces of information as part of an error all the way to implementing exponential backoff. While the error handling mechanisms in Go might on the surface seem simplistic, we can achieve quite rich handling using these custom errors to handle both common and uncommon situations.

由于error接口只是一种方法,因此我们已经看到,在为不同情况提供不同类型的错误方面,我们具有极大的灵活性。 这可以涵盖从传递多个信息(作为错误的一部分)一直到实现指数补偿的所有过程 。 虽然Go的错误处理机制从表面上看似乎很简单,但是我们可以使用这些自定义错误来处理常见和不常见的情况,从而实现非常丰富的处理。

Go has another mechanism to communicate unexpected behavior, panics. In our next article in the error handling series, we will examine panics—what they are and how to handle them.

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

翻译自: https://www.digitalocean.com/community/tutorials/creating-custom-errors-in-go

go 创建自定义包


http://www.niftyadmin.cn/n/3649767.html

相关文章

VM打开Linux蓝屏重启问题

在VM中配置好虚拟机,打开时报错。 报错信息:在该系统上全局禁用了虚拟打印功能,不会为该虚拟机启动此功能。虚拟设备“seriaIO”将开始断开连接。 解决办法:把虚拟机配置中的打印设备移除就可以了。

[MSN谈话] 关于技术和人生道路的问题

[MSN谈话] 关于技术和人生道路的问题网友 说:想向你请教下关于技术和人生道路的问题。话入正题吧,现在就是对做技术开发有点茫然。不知道是不是真的有前途或者有自己的奋斗目标。我本身来讲并不是对技术有狂热爱好的人,但是却进入了这个行业。精于心,简于…

spring react_使用React Spring在React中介绍动画

spring reactIn this article we’ll be exploring one of the best animation frameworks for React: React Spring. You’ll learn some of the basics behind changing you component styling into smooth, physics-based transitions. 在本文中,我们将探索React…

中国大学的集体堕落---麒麟vs汉芯

贴文不说话:“今年年初的时候,我发现FreeBSD的官方网站以及全球最大的开源网站SourceForge曾经一度被封,并且对于FreeBSD使用的是关键字过滤的极端方式封锁。联系这一系列事件,我不得不对年初的FreeBSD被封事件产生了一个可怕的怀…

[收藏]该怎么做,才能取得转型的成功呢?[人力资本]

看了《人力资本》这期的转型专题,给出了几个成功人士的故事,虽然有点老生常谈,不过作为辅助教材,还是有很大指导意义的。刘述尧作为技术人员转型的标杆,谈的感想都蛮现实的:“我上学时候的理想就是将来创办…

命令行 上下箭头符号_命令行基础知识:符号链接

命令行 上下箭头符号Symbolic links allow you to links files and directories to other files and directories. They go by many names including symlinks, shell links, soft links, shortcuts, and aliases. At a glance, a symbolic link looks like just a file or dir…

golang 延迟_了解Go中的延迟

golang 延迟介绍 (Introduction) Go has many of the common control-flow keywords found in other programming languages such as if, switch, for, etc. One keyword that isn’t found in most other programming languages is defer, and though it’s less common you’…

如何安装svelte_在Svelte中使用if块

如何安装svelteIn this tutorial, we’ll see how to use the if blocks with the Svelte framework to add some flow control logic to our Svelte markup code. 在本教程中,我们将看到如何在Svelte框架中使用if块向Svelte标记代码添加一些流控制逻辑。 Blocks i…