go错误处理error和panic

总结来源于《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导致程序奔溃。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容