如何处理错误
在示例当中已经很常用到错误。 value,err : method()。这样我们就接收到错误值。当然没有错误值时,函数一般返回nil. err中的错误信息,能够被打印出来。error在标准库中也是一个类型。同样有error类型有自己的定义。而具体每个具体的错误类型,再去实现error接口类型所定义的方法。然后再使用的时候,统一使用。
一个常见的使用错误的示例:
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("/test.txt") //返回值中第二个值为错误类型。
if err != nil {
fmt.Println(err) //Println函数,会调用传入的error类型的Error方法,进行错误信息的打印。Error方法返回一个string。
return
}
fmt.Println(f.Name(), "opened successfully")
}
输出:
open /test.txt: No such file or directory
按照 Go 的惯例,在处理错误时,通常都是将返回的错误与 nil 比较。nil 值表示了没有错误发生,而非 nil 值表示出现了错误。在我们的例子里,第 10 行检查了错误值是否为 nil。如果不是 nil,我们会简单地打印出错误,并在 main 函数中返回。
error接口的定义方法:
type error interface {
Error() string
}
error 有了一个签名为 Error() string 的方法。所有实现该接口的类型都可以当作一个错误类型。Error() 方法给出了错误的描述。
fmt.Println 在打印错误时,会在内部调用 Error() string 方法来得到该错误的描述。上一节示例中的第 11 行,就是这样打印出错误的描述的。
使用错误: 从错误中获取更多信息
在以上的例子当中我们仅仅只是获取到错误,然后判断是否存在错误。 然后将错误信息整体打印出来。而没有办法针对不同的错误信息,做一些更细化的提示或业务处理。
而我们希望能够获取到更多的错误信息,已加入更多针对错误的细致处理。
一般的方法,都会再后续中介绍:
1)对于类型再已知范围内的一些类错误值,一般使用类型断言表达式或类型switch语句判断
2)对于已有相应变量且类型相同的一些列错误值,一般直接使用判等操作。
3)对于没有相应变量值且类型未知的错误,只能使用其错误信息的字符串表示形式来做判断。
获取方法1: 断言底层类型,使用结构体获取更多信息。
(断言:就是通过接口类型。向下获取到底层具体实现类型的值。然后能够使用具体实现类型的属性和方法。常用的格式为i.(T) i为接口类型值。T为具体类型值。 e = err.(*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 方法,实现了 error 接口。
Error() string 将文件操作、路径和实际错误拼接,并返回该字符串。于是我们得到该错误信息:open /test.txt: No such file or directory
我们可以看到结构体中包含有属性Path。那么我们就能够通过断言的形式,获取到底层的类型值。然后就能够使用结构体中的属性值,提供更多的错误信息了。
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("/test.txt")
if err, ok := err.(*os.PathError); ok { // 进行断言。ok,表示断言成功。在断言的时候,如果类型"不一致",断言失败。将会返回false。err = PathError
fmt.Println("File at path", err.Path, "failed to open") // 这里就能够使用PathErro中的属性值Path.Op了。
return
}
fmt.Println(f.Name(), "opened successfully")
}
使用断言后的方法
查看另一个错误类型的结构体:
type DNSError struct {
...
}
func (e *DNSError) Error() string {
...
}
func (e *DNSError) Timeout() bool { //结构体方法,返回bool类型。
...
}
func (e *DNSError) Temporary() bool { //结构体方法,返回bool类型。
...
}
其中包含了多个结构体类型的方法,返回bool类型。
从上述代码可以看到,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() { //使用断言后的方法,返回一个bool类型。我们就可以使用这个方法,来判断具体的错误原因。因此达到获取更新错误信息。
fmt.Println("operation timed out")
} else if err.Temporary() { //同样返回一个bool类型。
fmt.Println("temporary error")
} else {
fmt.Println("generic error: ", err)
}
return
}
fmt.Println(addr)
}
在上述程序中,我们在第 9 行,试图获取 golangbot123.com(无效的域名) 的 ip。在第 10 行,我们通过 *net.DNSError 的类型断言,获取到了错误的底层值。接下来的第 11 行和第 13 行,我们分别检查了该错误是由超时引起的,还是一个临时性错误。
获取方法2:直接比较
函数返回的错误值,就是某个错误类型的对象值。而我们并不知道这个错误的详细类型。而我们知道使用的类型的错误类型定义。那此时我们就能够使用比较的形式,来判断出具体的错误类型。
filepath 包中的 Glob函数用于返回满足 glob 模式的所有文件名。如果模式写的不对,该函数会返回一个错误 ErrBadPattern。
filepath 包中的 ErrBadPattern 定义如下:
var ErrBadPattern = errors.New("syntax error in pattern")
使用直接判断的示例:
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)
}
上述程序里,我们查询了模式为 [ 的文件,然而这个模式写的不正确。我们检查了该错误是否为 nil。为了获取该错误的更多信息,我们在第 10 行将 error 直接与 filepath.ErrBadPattern 相比较。如果该条件满足,那么该错误就是由模式错误导致的。
获取方法3: 对错误的具体类型已知。使用swith来做判断。
使用断言同样需要我们知道具体的错误类型的字段和方法。而我们同样能够通过判断返回的错误类型,来获取到错误信息。比如标准包os中的使用方式:
func underlyingError(err error)error {
swithc err := err.(type) {
case *os.PathError:
return err.Err
case *os.LinkError:
return err.Err
case *os.SyscallError:
reutnr err.Err
case *exec.Error:
return err.Err
}
return err
}
swithc case的代码结构同样能够用在知道具体的错误值的方式.在os包中,这些值是已经提前定义好的错误值。
swithc err {
case os.ErrClosed:
return err.Err
case os.ErrInvalid:
return err.Err
case os.ErrPermission:
reutnr err.Err
}
不能忽略程序中的错误处理环节。
小结:
- 错误类型接口定义。需要实现一个Error()string方法。其他的错误类型都基于此接口重新定义出来的。同样可以自己定义一个新的错误类型出来。
- 如何获取更多的错误信息,通过断言获取底层错误类型。然后使用错误类型的字段、方法。来进行更多的错误信息判断。
- 如何获取更多的错误信息,通过直接和结构类型的错误定义进行比较。
- 一定不能够忽略错误处理。
参考链接:
https://blog.csdn.net/cbmljs/article/details/83064062