goalng知识点-错误处理

如何处理错误

        在示例当中已经很常用到错误。 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

转载于:https://my.oschina.net/u/4156317/blog/3069772

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值