error
error是go自带的一个接口,只有一个Error()方法,没有入参,有一个string类型的出参。
go自带了很多实现了error接口的struct,如errorString、TimeoutError、DNSError等。
errorString只有一个string型的属性,我们可以通过errors包的New(text string)函数获取指向errorString实例的指针。示例如下:
func Check(i int) (int, error) { if i < 0 { return -1, errors.New("negative") } else { return 1, nil } } func main() { i, err := Check(-1) if err != nil { fmt.Println("something wrong") } else { fmt.Println("going on,i=", i) } }
error实例能否用==比较?
github.com/pkg/errors是一个比较好用的error相关的包。
panic
panic是个函数,有一个空接口类型的入参(可传任意类型,如字符串、error等等),无出参,作用类似于java中的throw new RuntimeException(msg),panic之后的代码不执行,程序会非零退出。
func main() { fmt.Println(1) panic("something wrong") fmt.Println(2) }
只会打印1,不会打印2,main函数非零退出。
recover
recover也是个函数,没有入参,出参是个空接口类型,具体返回什么类型取决于使用的场景:
如果recover()在defer后面的函数中调用,则如果goroutine panic了,则recover()函数返回panic函数的入参,同时panic会被压制住,程序不会非零退出了,否则返回nil。如果不是在defer后面的函数中调用,则返回nil,且不会压制panic。
recover()函数不包装进一个函数中调用,而是直接在defer后面调用的话,没有任何效果。
recover()函数作用有点像java中的catch,因为它在一些情况下可以压制panic。但不是完全一样。因为recover()函数在类似finally块中执行才有意义,而catch没有在finally块中。
示例如下:
func MayPanic(i int) int { if i == 0 { panic("不能为0") } else if i < 0 { panic(errors.New("不能小于0")) } return 1 } func main() { defer func() { if r := recover(); r != nil { fmt.Println(r) fmt.Println(reflect.TypeOf(r)) } }() //i := MayPanic(0) i := MayPanic(-1) fmt.Println(i) }
上例中,recover()在defer后面的函数中调用,且协程执行panic(errors.New("不能小于0"))而panic了,所以recover()函数返回error。main函数不会非零退出。
func main() { func() { if r := recover(); r == nil { fmt.Println(r) fmt.Println(reflect.TypeOf(r)) } }() i := MayPanic(1) fmt.Println(i) }
上例中,recover()函数虽然在defer后面的函数中调用,但是协程没有panic,所以recover()函数返回nil。
func MayPanic(i int) int { if i == 0 { panic("不能为0") } else if i < 0 { panic(errors.New("不能小于0")) } return 1 } func main() { func() { if r := recover(); r == nil { fmt.Println(r) fmt.Println(reflect.TypeOf(r)) } }() i := MayPanic(-1) fmt.Println(i) }
上例中,recover()不是在defer后面的函数中调用的,所以返回值是nil。