总结来源于《go in practice》
1、自定义struct,并实现error接口
type ParseError struct {
Message string
Line, Char int
}
func (p *ParseError) Error() string {
format := "%s o1n Line %d, Char %d"
return fmt.Sprintf(format, p.Message, p.Line, p.Char)
}
这种错误处理,可以丰富error打印出的信息。但是如果一个程序当中要处理不同类型的错误的时候,就需要使用错误变量来完成:
package main
import (
"errors"
"fmt"
"math/rand"
"time"
)
var ErrTimeout = errors.New("the request timed out")
var ErrReject = errors.New("the request was rejected")
var random = rand.New(rand.NewSource(time.Now().UnixNano()))
func SendRequest(req string) (string, error) {
switch random.Int() % 3 {
case 0:
return "Success", nil
case 1:
return "", ErrReject
default:
return "", ErrTimeout
}
}
func main() {
res, err := SendRequest("Hello")
for err == ErrTimeout {
fmt.Println("Timeout. Retring.")
res, err = SendRequest("Hello")
}
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res)
}
}
通过error变量定义错误类型,使用很方便。另一个问题是什么时候使用panic,以及panic中传入什么参数合适?首先panic和error区别:panic是程序执行时碰到无法预料的情况,panic引起程序堆栈解开,程序执行失败。例如让程序执行一个除数是0的计算,如果不处理就会导致:panic: runtime error: integer divide by zero。要让程序正常执行可以对除数检查,然后返回error来解决。所以在程序开发中尽可能适应error来处理预期的错误。另一种解决方法就是使用recover,从panic中恢复。使用defer来捕获panic,让程序正常运行
package main
import (
"errors"
"fmt"
)
func yikes() {
panic(errors.New("something bad hanppen"))
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Printf("Trapped panic: %s (%T)\n", err, err)
}
}()
yikes()
}
panic传入错误类型是惯用方法。panic(errors.New("Something bad happened.")),recover返回值是就是传入panic的值,默认返回nil。defer使用场景:文件、数据库连接、网络连接的关闭、解锁以及panic的捕获等。panic处理网络服务器错误:
package main
import (
"bufio"
"errors"
"fmt"
"net"
)
func listen() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Failed to open port on 8080")
return
}
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connect")
}
go handle(conn)
}
}
func response(b []byte, conn net.Conn) {
conn.Write(b)
panic(errors.New("pretend I'm a real error"))
}
func handle(conn net.Conn) {
defer func() {
if err := recover(); err != nil {
fmt.Printf("Fatal error: %s", err)
}
conn.Close()
}()
reader := bufio.NewReader(conn)
data, err := reader.ReadBytes('\n')
if err != nil {
fmt.Println("Failed to read from socket.")
conn.Close()
}
response(data, conn)
}
func main() {
listen()
}
这里在handle函数里添加defer捕获response函数返回的panic,避免服务器故障。不能因为一个应答错误,导致其他的网络连接也中断。在http包中,
启动http服务器时,标准库当中已经做了故障捕获处理。
type GoDoer func()
func Go(todo GoDoer) {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic in safely.Go: %s", err)
}
}()
todo()
}()
}
这个用例提供了一个panic处理,目的是为了启动goroutine运行todo函数,但是如果todo报panic的话会导致程序退出。defer可以很好的实现捕获功能。可以使用这个函数来执行用户定义的任何函数,而不需要担心panic导致程序奔溃。