zoukankan      html  css  js  c++  java
  • Golang进阶之路(四),标准错误和异常

    感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!感谢参考原文-http://bjbsair.com/2020-04-01/tech-info/18320.html

    标准错误

    Go语言内置的error接口,自定义的类型,只要实现该接口方法即可称为标准错误类型,来看看源码:

    // The error built-in interface type is the conventional interface for  
    // representing an error condition, with the nil value representing no error.  
    type error interface {  
      Error() string  
    }
    

    自定义一个错误类型,实现error接口的Error()方法:

    type MyError struct {  
      s string  
    }  
    func (myError MyError) Error() string {  
      return "MyError"  
    }  
    func main() {  
      var e MyError = MyError{"err"}  
      fmt.Println(e)  // 输出:MyError  
      err, ok := interface{}(e).(error)  
      fmt.Println(ok)  // 输出:true  
      fmt.Println(err)  // 输出:MyError  
    }
    

    从上面例子来看,err, ok := interface{}(e).(error)中的类型转换结果为true,说明自定义的类型就是error的子类型,也就证实了只要某个类型实现了Error()方法,那这个类型就是error类型。

    errors包

    Go语言的errors包有个内置的错误类型叫errorString,来看一下源码:

    package errors  
      
      
    // New returns an error that formats as the given text.  
    // Each call to New returns a distinct error value even if the text is identical.  
    func New(text string) error {  
      return &errorString{text}  
    }  
      
      
    // errorString is a trivial implementation of error.  
    type errorString struct {  
      s string  
    }  
      
      
    func (e *errorString) Error() string {  
      return e.s  
    }
    

    从上面的源码看到,errorString类型实现了error接口的Error()方法,因此errorString类型就是error的子类型。

    我们知道,当某个包下面的变量和方法名以小写开头时,这个变量或者方法只能在这个包下面才能访问。因此,我们自己写的代码没办法直接创建errorString的实例,我们可以使用errors里的New方法方便的创建error类型。

    var e = errors.New("MyError")  
    fmt.Println(e)  // 输出:MyError  
    err, ok := interface{}(e).(error)  
    fmt.Println(ok)  // 输出:true  
    fmt.Println(err)  // 输出:MyError
    

    和最上面的例子一样,err, ok := interface{}(e).(error)中的类型转换结果为true,说明errorString就是error的子类型。

    创建标准错误

    func f1(arg int) (int, error) {  
        if arg == 42 {  
            return -1, errors.New("can't work with 42")  // 使用错误提示创建标准错误  
        }  
        return arg + 3, nil  
    }  
      
      
    type argError struct {  
        arg  int  
        prob string  
    }  
      
      
    func (e *argError) Error() string {  // 自定义错误类型需要实现Error函数  
        return fmt.Sprintf("%d - %s", e.arg, e.prob)  
    }  
    func f2(arg int) (int, error) {  
        if arg == 42 {  
            return -1, &argError{arg, "can't work with it"}  
        }  
        return arg + 3, nil  
    }  
    func main() {  
        for _, i := range []int{7, 42} {  
            if r, e := f1(i); e != nil {  
                fmt.Println("f1 failed:", e)  // f1 failed: can't work with 42  
            } else {  
                fmt.Println("f1 worked:", r)  
            }  
        }  
        for _, i := range []int{7, 42} {  
            if r, e := f2(i); e != nil {  
                fmt.Println("f2 failed:", e)  
            } else {  
                fmt.Println("f2 worked:", r)  
            }  
        }  
        _, e := f2(42)  
        if ae, ok := e.(*argError); ok {  
            fmt.Println(ae.arg)  
            fmt.Println(ae.prob)  
        }  
    }  
    
    

    格式化消息的标准错误类型

    fmt包中有个Errorf方法,我们可以通过它创建一个具有格式化字符串的标准错误类型,来看一下fmt.Errorf的源码:

    func Errorf(format string, a ...interface{}) error {  
      p := newPrinter()  
      p.wrapErrs = true  
      p.doPrintf(format, a)  
      s := string(p.buf)  
      var err error  
      if p.wrappedErr == nil {  
        err = errors.New(s)  
      } else {  
        err = &wrapError{s, p.wrappedErr}  
      }  
      p.free()  
      return err  
    }
    

    可以看到,fmt.Errorf里面使用errors.New来创建error类型的实例,然后把格式化后字符串放入这个实例里面。典型使用案例:

    const name, id = "bimmler", 17  
    err := fmt.Errorf("user %q (id %d) not found", name, id)  // user="bimmler" (id=17) not found  
    if err != nil {  
      fmt.Print(err)  
    }
    

    这样,err不仅包含了格式化的错误消息,而且还是error类型,比我们先用fmt.Printf再用errors.New的写法会更简洁。

    天杀错误判断和处理逻辑

    刚从其他语言转到Go语言时,会非常不习惯Go语言的错误处理逻辑。在Java和Python里面,我们可以使用throw来抛出异常,在另一个地方使用try...catch来捕获异常。而Go语言鼓励大家把错误放到返回值里面,但是呢,又提供了panic和recover来抛出和捕获异常。一般来说,我们把返回值里面返回的叫做错误(因为他们都是error类型),把抛出和捕获的叫做异常。

    既然把错误放到返回值里,那我们就每次都需要判断返回里的错误是否为空,才能知道是不是正常返回了。判断和处理错误需要用到if,如果一段代码里面调用了很多函数,我们就能看到一连串的if,这种写法感觉简直就是疯了!

    result1, err := err1()  
    if err != nil {  
      fmt.Println(result1)  
    }  
    result2, err := err2()  
    if err != nil {  
      fmt.Println(result2)  
    }  
    result3, err := err3()  
    if err != nil {  
      fmt.Println(result3)  
    }  
    result4, err := err4()  
    if err != nil {  
      fmt.Println(result4)  
    }
    

    不过这个没法避免,老老实实习惯一下吧!

    panic/recover/defer

    对于异常,Go语言可以使用panic来抛出一个恐慌性异常,一般来说,当程序遇到了比较大的问题,没有办法再执行下去,我们就用panic来抛出异常。来看一下panic源码:

    func panic(v interface{})
    

    panic接收一个interface{}类型的对象,我们上篇文章说过了,任何类型都是interface{}类型的子类型,因此,panic可以抛出任何对象。使用案例:

    func main() {  
      panic("hello")  
    }
    

    输出:

    panic: hello  
      
    goroutine 1 [running]:  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:4 +0x40  
    
    

    同时,我们可以使用recover来捕获那些被panic抛出对象:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      p := pa()  
    fmt.Println(p)  
    if r := recover(); r != nil {  
       fmt.Println(r)  
      }  
    }
    

    输出:

    panic: me  
      
    goroutine 1 [running]:  
    main.pa(...)  
        E:/goworkspace/blog/src/main/main.go:6  
    main.main()  
        E:/goworkspace/blog/src/main/main.go:9 +0x40
    

    怎么好像recover没起作用?这当然了,recover需要与defer一起使用,而且,recover的使用要放在panic抛出之前。下面的例子才对:

    func pa() string {  
      panic("me")  
    }  
    func main() {  
      defer func() {  
        if r := recover(); r != nil {  
          fmt.Println(r)  // 输出:me  
        }  
      }()  
      p := pa()  // 此行panic抛出me字符串  
      fmt.Println(p)  // 此行不会执行  
    }
    

    defer的用法

    defer关键字表示,当前这个函数在退出之前,执行一下defer后面的逻辑。相当于我们委托一些操作给Go语言,在函数退出之前执行,有点像Java和Python里面的finally作用了。

    这样,我们就可以把一些一定要执行的操作,放在defer中了,比如,我们关闭某个已经打开的资源:

    fsrc, err := os.Open("source.txt")  
    if err != nil {  
      fmt.Println("open source file failed")  
      return  
    }  
    defer fsrc.Close()  // 第一个defer  
    fdes, err := os.Open("target.txt")  
    if err != nil {  
      fmt.Println("open target file failed")  
      return  
    }  
    defer fdes.Close()  // 第二个defer  
    fmt.Println("do something here")
    

    如果一个函数里面有多个defer,那么在函数推出之前,会先执行最后一个defer,然后再执行倒数第二个、倒数第三个...,就像后进先出栈的操作。

    我们下一篇聊聊通道和goroutine。

    喜欢的点个关注!

  • 相关阅读:
    lua源码分析 伪索引
    visual studio 插件
    修改Linux内核参数 减少TIME-WAIT
    linux下编译libmysqlclient, 安装mysql-server mysql-client
    编译静态库tinyxml2
    linux下编译lua库
    在Xshell中文件内容显示乱码
    Java中的自增自减
    Integer的缓存机制
    八大基本排序
  • 原文地址:https://www.cnblogs.com/lihanlin/p/12618982.html
Copyright © 2011-2022 走看看