[译] Part 30: Golang 中的Error处理

咔叽咔叽_7647 · · 656 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

什么是Error?

Error表示程序中的异常情况。假设我们正在尝试打开文件,文件系统中不存在该文件,那么这是一种异常情况,它就代表一种error
Go中使用内置的error类型表示错误。
就像任何其他的内置类型,如int,float64,... error可以存储在变量中,从函数返回等等。

例子

用打开了一个不存在的文件的示例程序来解释一下。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Open("/test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(f.Name(), "opened successfully")
}

Run in playground

上面的程序中,我们试图在路径/test.txt中打开文件。 os包的Open函数定义如下,

func Open(name string) (file *File, err error)

如果文件被成功打开,则Open函数将会返回打开的文件,同时err为nil。如果在打开文件时出错,err将返回非nil的错误。

如果一个函数或者方法返回错误,那么按照惯例,函数返回值的最后面那一个值就是err。所以,Open函数返回的最后一个值是err。

在Go中处理错误的惯用方法是将返回的错误与nil进行比较。 如果返回的err是nil,则表示没有发生错误,非nil值则表示存在错误。在我们的例子中,我们在第10行检查错误的返回值,如果它不是nil,我们只需打印错误并从main函数返回。

运行上述程序将打印,

open /test.txt: No such file or directory  

完美????。我们打印了一条错误消息,显示了该文件不存在。

Error type

让我们再深入一点,看看如何定义内置的错误类型。 error是具有以下定义的接口类型,

type error interface {  
    Error() string
}

它包含一个Error方法,实现此接口的Error就可以被用作error。此方法提供了error的描述

当打印error时,fmt.Println函数在内部调用Error方法以获取error的描述。上述例子的第11行就展示了error描述的打印。

如何从error中提取更多的信息

在上面的例子中我们看到打印了错误的描述。如果我们想要导致error的文件的实际路径,该怎么办?一种可能的方法是解析错误字符串。这是输出,

open /test.txt: No such file or directory 

我们可以获得错误内容并获取导致错误的文件的文件路径为“/test.txt”,但这不是一种很好的方式。在有些情况下,我们应该捕获更多的错误信息,然后根据不同的错误去做区别处理。(类似其他的语言catch多个错误)

有没有办法可靠地获取文件名?答案是肯定的,标准的Go库使用不同的方式来提供有关error的更多信息。让我们逐一看看它们。

1. 从底层结构类型的字段中获取更多信息

如果仔细阅读Open函数的文档,可以看到其error返回类型为*PathErrorPathError是一种结构类型,它在标准库中的实现如下,

type PathError struct {  
    Op   string
    Path string
    Err  error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }  

如果有兴趣知道上述源代码的位置,可以在此处找到https://golang.org/src/os/error.go?s=653:716#L11

从上面的代码中,可以知道* PathError通过声明Error方法来实现error的接口。此方法返回一个包含路径,实际错误拼接的字符串并返回。因此我们收到了这个错误内容,

open /test.txt: No such file or directory  

PathError structPath字段包含导致错误的文件的路径。让我们修改上面编写的程序并打印路径。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Open("/test.txt")
    if err, ok := err.(*os.PathError); ok {
        fmt.Println("File at path", err.Path, "failed to open")
        return
    }
    fmt.Println(f.Name(), "opened successfully")
}

Run in playgroud

在上面的程序中,我们在第10行从结构字段中去获取error的基础值。然后我们在11行使用err.Path打印路径。 输出如下,

File at path /test.txt failed to open 

我们已成功使用结构字段从错误中获取文件路径。

2. 使用基础结构类型的方法获取更多信息

第二种方法是通过调用struct类型的方法获取更多信息。

让我们通过一个例子更好地理解这一点。

标准库中的DNSError结构类型定义如下,

type DNSError struct {  
    ...
}

func (e *DNSError) Error() string {  
    ...
}
func (e *DNSError) Timeout() bool {  
    ... 
}
func (e *DNSError) Temporary() bool {  
    ... 
}

从上面的代码中可以看出,DNSError结构有两个方法TimeoutTemporary,它们返回一个布尔值,指示error是由于超时还是暂时的。

让我们编写一个包含* DNSError类型的程序,并调用这些方法来确定error是暂时错误类型还是超时错误类型。

package main

import (  
    "fmt"
    "net"
)

func main() {  
    addr, err := net.LookupHost("golangbot123.com")
    if err, ok := err.(*net.DNSError); ok {
        if err.Timeout() {
            fmt.Println("operation timed out")
        } else if err.Temporary() {
            fmt.Println("temporary error")
        } else {
            fmt.Println("generic error: ", err)
        }
        return
    }
    fmt.Println(addr)
}

注意:LookupHost在playground上不起作用。请在本地计算机上运行此程序。

在上面的程序中,我们在第9行试图获取一个无效域名golangbot123.com的IP地址。我们通过* net.DNSError来获取错误的基础值。然后我们在第10和13行分别检查error是由于超时还是临时引起的。

在我们的例子中,错误既不是暂时的也不是由于超时,因此程序将打印,

generic error:  lookup golangbot123.com: no such host 

如果错误是临时的或由于超时,则执行相应的if语句,这样就可以分情况适当地处理它了。

3. 直接比较

获取有关错误的更多详细信息的第三种方法是直接与类型错误的变量进行比较。让我们通过一个例子来理解这一点。

filepath包的Glob函数用于返回与正则模式匹配的所有文件的名称。格式错误时,此函数返回错误ErrBadPattern

ErrBadPatternfilepath包中定义如下,

var ErrBadPattern = errors.New("syntax error in pattern")

errors.New用于创建一个新的error。我们将在下一个教程中详细讨论这个问题。

当匹配发生格式错误时,Glob函数返回ErrBadPattern

让我们编写一个小程序来检查这个error

package main

import (  
    "fmt"
    "path/filepath"
)

func main() {  
    files, error := filepath.Glob("[")
    if error != nil && error == filepath.ErrBadPattern {
        fmt.Println(error)
        return
    }
    fmt.Println("matched files", files)
}

Run in palygroud

在上面的程序中,我们匹配带有[的文件,这是一个格式错误的方式。我们检查error是否为nil。要获得有关error的更多信息,我们在第10行直接将它与filepath.ErrBadPattern进行比较。如果条件满足,则是因为格式错误。所以该程序将输出,

syntax error in pattern 

标准库使用上述方法提供有关error的更多信息。我们将在下一个教程中使用这些方法来创建自己的自定义错误。

不要忽视Error

永远不要忽视error。忽略error会引发麻烦。让我重写一个示例,该示例忽略了error处理。

package main

import (  
    "fmt"
    "path/filepath"
)

func main() {  
    files, _ := filepath.Glob("[")
    fmt.Println("matched files", files)
}

Run in playground

我们从前面的例子中已经知道匹配是无效的。我在第9行通过使用_标识符忽略了Glob函数返回的error。然后在第10行打印该匹配的文件。程序将打印,

matched files [] 

由于我们忽略了error,似乎没有输出匹配文件,但实际上是模式本身的格式不正确,这样就不知道到底是什么导致泐该 error的产生。所以不要忽视error

在本教程中,我们讨论了如何处理程序中发生的error以及如何检查error以从中获取更多信息。

在下一个教程中,我们将创建自己的自定义 error,并为标准error添加更多上下文。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:咔叽咔叽_7647

查看原文:[译] Part 30: Golang 中的Error处理

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

656 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传