迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 >

Go 语言 错误处理详细介绍

作者:迹忆客 最近更新:2021/11/04 浏览次数:

关于 Go 语言中的错误的基本介绍可以参考我们的GO教程中的Go 语言错误处理。这里我们继续对Go中的错误处理做进一步的介绍。

让我们通过一段代码来开始我们的旅程

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")
}

运行上面的程序会打印如下错误内容

open /test.txt: No such file or directory  

错误类型表示

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

type error interface {  
    Error() string
}

它包含一个签名方法 Error() string。 任何实现此接口的类型都可以用作错误。 此方法提供错误的描述。

打印错误时,fmt.Println 函数在内部调用 Error() string 方法来获取错误的描述。

从错误中提取更多信息的不同方法

现在我们知道 error 是一种接口类型,让我们看看如何提取有关错误的更多信息。

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

open /test.txt: No such file or directory  

我们可以解析此错误消息并获取导致错误的文件路径,但这是一种非常不友好的做法。 在较新版本的 Go 中,错误描述可以随时更改,我们的代码将中断。

有没有更好的方法来获取文件名🤔? 答案是肯定的,这是可以做到的,Go 标准库使用不同的方式来提供有关错误的更多信息。 让我们对它们一一进行介绍。

1. 对底层结构类型断言并从结构字段中获取更多信息

如果你仔细阅读 Open 函数的文档,你会发现它返回一个 *PathError 类型的错误。 PathError 是一个结构体类型,它在标准库中的实现如下,

type PathError struct {  
    Op   string
    Path string
    Err  error
}

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

从上面的代码可以了解到 *PathError 通过声明 Error() string 方法来实现错误接口。 此方法连接操作、路径和实际错误并返回它。 因此我们得到了错误信息

open /test.txt: No such file or directory  

PathError 结构的 Path 字段包含导致错误的文件的路径。 我们修改上面写的程序,打印路径。

package main

import (  
    "fmt"
    "os"
)

func main() {  
    f, err := os.Open("test.txt")
    if err != nil {
        if pErr, ok := err.(*os.PathError); ok {
            fmt.Println("Failed to open file at path", pErr.Path)
            return
        }
        fmt.Println("Generic error", err)
        return
    }
    fmt.Println(f.Name(), "opened successfully")
}

运行示例

在上面的程序中,我们首先检查错误是否不是 nil。 然后我们使用类型断言来获取错误接口的底层值。 然后我们使用 pErr.Path 打印路径。 上述程序输出如下

go error path

如果底层错误不是 *os.PathError 类型,则程序将打印一般错误消息。

太棒了😃。 我们已经成功地使用类型断言从错误中获取文件路径。

2.断言底层结构类型并使用方法获取更多信息

从错误中获取更多信息的第二种方法是断言底层类型并通过调用结构体类型上的方法获取更多信息。

让我们通过一个例子来理解这一点。

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

type DNSError struct {  
    ...
}

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

DNSError 结构体有两个方法 Timeout() boolTemporary() bool ,它们返回一个布尔值,指示错误是由于超时还是临时错误。

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

package main

import (  
    "fmt"
    "net"
)

func main() {  
    addr, err := net.LookupHost("jiyik910.com")
    if err != nil {
        if dnsErr, ok := err.(*net.DNSError); ok {
            if dnsErr.Timeout() {
                fmt.Println("operation timed out")
                return
            }
            if dnsErr.Temporary() {
                fmt.Println("temporary error")
                return
            }
            fmt.Println("Generic DNS error", err)
            return
        }
        fmt.Println("Generic error", err)
        return
    }
    fmt.Println(addr)
}

注意: DNS 查找在我们的Go在线运行工具中不起作用。 请在您的本地机器上运行这个程序。

在上面的程序中,我们尝试获取无效域名 jiyik910.com 的IP地址。 我们通过将错误声明为类型 *net.DNSError 来获取错误的值。 然后我们检查错误是由于超时还是临时的。

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

go dnsError 结果

如果错误是暂时的或由于超时,那么相应的 if 语句就会执行,我们可以适当地处理它。

3. 直接比较

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

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

ErrBadPatternfilepath 包中定义为全局变量。

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

errors.New() 用于创建新错误。

当模式格式错误时,ErrBadPattern 由 Glob 函数返回。

让我们编写一个小程序来检查这个错误。

package main

import (  
    "fmt"
    "path/filepath"
)

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

运行示例

在上面的程序中,我们搜索模式 [ 这是一个格式错误的模式的文件。 我们检查错误是否不为 nil。 为了获得有关错误的更多信息,我们直接将其与 filepath.ErrBadPattern 进行比较。如果满足条件,则错误是由于格式错误造成的。 该程序将输出如下错误

go error 模式错误

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

不要忽略错误

永远不要忽略错误。 忽略错误会招来麻烦。 让我重写示例,该示例列出了与忽略错误处理代码的模式匹配的所有文件的名称。

package main

import (  
    "fmt"
    "path/filepath"
)

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

运行示例

我们已经从前面的例子中知道该模式是无效的。 我通过使用 _blank 识符忽略了 Glob 函数返回的错误。我只是打印匹配的文件。 该程序将打印如下信息

go ignore error

由于我们忽略了错误,输出似乎没有文件与模式匹配,但实际上模式本身格式不正确。 所以永远不要忽视错误。

除非注明转载,本站文章均为原创或翻译,欢迎转载,转载请以链接形式注明出处

本文地址:

迹忆客

专注技术分享,项目实战分享!

技术宅 乐于分享 7年编程经验
社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

热门文章

教程更新

热门标签

Go