错误就是程序中存在的异常情况。打开一个不存在的文件,这就是一个异常情况,也就是错误。
在 Go
中,错误也是一个值,它的类型为 error
。
正如其它的内建类型,如 int
、float64
… 我们可以把错误存储在变量中,也可以让函数返回错误。
接下来,我将写一个程序,这个程序试图打开一个不存在的文件。
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
函数 位于 os
包,它的定义如下:
func Open(name string) (file *File, err error)
Open
函数存在 2
种情况:
Open
函数 会返回文件句柄,此时错误为 nil
。Open
函数 会返回 nil
文件句柄,此时错误不为 nil
。在一般情况下,如果函数需要返回错误,错误必须位于最后一个返回值。根据这个规则,
Open
函数 将err
作为最后一个返回值。
在第 9
行,我们试图打开路径为 /test.txt
的文件。
在第 10
行,我们将 err
与 nil
进行比较。如果 err
不等于 nil
,我们就输出错误。
运行程序,输出如下:
open /test.txt: No such file or directory
如我们所想,我们得到了一个错误,该错误告诉我们:文件不存在。
我们深入一下,看看 error
类型 是如何定义的。
type error interface {
Error() string
}
从上面可以看出:error
是一个接口类型,它拥有一个 Error
方法。
任何实现了 error
接口 的类型,都可以作为错误。
在上面的样例程序中,为了获取错误的描述信息,fmt.Println
函数 内部会调用 Error() string
方法。
接下来,我们看看怎么从错误中提取更多信息。
在上面的例子中,我们只是输出了错误的描述信息。假如我们需要错误中的文件路径,我们应该怎么做呢?
上面是什么意思呢?
意思就是:之前的错误的描述信息是open /test.txt: No such file or directory
,而此时我们想要的是/test.txt
。
其中一种方式就是:从错误的描述信息中 (描述信息是一个字符串),提取出文件路径。
但这是一种很烂的方式,因为随着版本更新,错误的描述信息可能会发生改变。
有没有方法能可靠地获取文件名呢?答案是肯定的,通过 Go
的标准库,我们能获取到错误的更多信息。
接下来,我将一个一个地列举。
获得错误更多信息的第 1
种方法是:先断言错误的底层类型,再从该底层类型的字段中获得更多的信息。
如果你仔细阅读 Open
函数 的文档,你会发现: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() }
通过声明 Error
方法,*PathError
类型 实现了 error
接口。
PathError
结构 的 Path
字段 就是文件路径。
接下来,我们改写下之前的程序。在新的程序中,我们打印出错误信息的文件路径:
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")
}
在第 10
行,通过类型断言,我们得到了错误接口的底层对象。
程序输出如下:
File at path /test.txt failed to open
使用类型断言,我们成功地从错误中提取出文件路径。
获得错误更多信息的第 2
种方法是:先断言错误的底层类型,再调用该底层类型的方法,进而获取更多信息。
通过例子理解吧~
在标准库中,DNSError
结构 的定义如下:
type DNSError struct {
...
}
func (e *DNSError) Error() string {
...
}
func (e *DNSError) Timeout() bool {
...
}
func (e *DNSError) Temporary() bool {
...
}
DNSError
的 Timeout() bool
、Temporary() bool
方法,分别会告诉我们:错误是否是超时产生的、错误是否是临时的。
接下来,我们来写个程序。在程序中,我们会先将错误断言为 *DNSError
类型,之后通过调用 DNSError
结构 的 Timeout() bool
、Temporary() bool
方法,判断错误是否是超时产生的,以及是否是临时。
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)
}
在上面程序的第 9
行,我们试着去获取一个非法域名 (golangbot123.com
) 的 IP
地址。
在第 10
行,通过将错误接口断言为 *net.DNSError
,我们获取到了错误接口的底层对象。
在第 11
、13
行,我们分别判断错误是否是超时产生的,以及是否是临时。
程序输出如下:
generic error: lookup golangbot123.com: no such host
上面的例子也告诉我们:我们可以根据错误的产生原因,对症下药。
获得错误更多信息的第 3
种方法是:直接比较。
让我们通过例子来理解这种方法。
filepath
包 有一个 Glob
函数,这个函数能返回一些 与模式串匹配的 文件名。当模式串格式错误时,Glob
函数 会返回一个错误变量 — ErrBadPattern
。
ErrBadPattern
变量 被定义在 filepath
包 中。
var ErrBadPattern = errors.New("syntax error in pattern")
errors.New
会创建一个新的错误。
当模式串格式错误时,Glob
函数 会返回 ErrBadPattern
变量。通过这个特点,我们来写个小程序。
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)
}
在上面的程序中,模式串[
的格式是错误的。
在第 10
行,为了获取更多错误信息,我们直接将 Glob
函数返回的错误 与 filepath.ErrBadPattern
变量 进行比较。如果它们相等,则表示 Glob
函数返回的错误 是 格式错误。
程序输出如下:
syntax error in pattern
标准库会通过以上提及的方法,为我们提供更多错误信息。当自定义错误时,我们也会用到这些方法。
不要忽略错误!忽略错误会带来麻烦!
接下来,我重写下上面的例子 (就是 Glob
函数 的那个例子)。这次,我们不进行错误处理。
package main
import (
"fmt"
"path/filepath"
)
func main() {
files, _ := filepath.Glob("[")
fmt.Println("matched files", files)
}
如前所述,模式串[
是非法的。
在第 9
行,通过使用 空白标识符_
,我们忽略了 Glob
函数返回的错误。
在第 10
行,我们将匹配的文件名输出。
程序输出如下:
matched files []
从输出中可以看出,此时没有 与模式串匹配的 文件名。但实际上,这是因为模式串 格式错误 导致的。
由于忽略了错误处理,对于上面的输出,我们不能确定:到底是没有 与模式串匹配的 的文件名,还是 出现了错误。
所以,请不要忽略错误处理!!!
这就是全部内容了~
祝你每天都开心~
优质内容来之不易,您可以通过该 链接 为我捐赠。
感谢原作者的优质内容。
欢迎指出文中的任何错误。