go 中的错误处理主要使用到error
和 panic
,其中 error
使用居多
error
主要有以下几个特点
error
仅仅是一个返回值
panic
主要有以下几个特点
panic
,所以无法确保调用者一定会处理尽可能提前定义好所有需要的错误类型及错误代码,方便业务中使用及判断
// go/1.17.5/libexec/src/bufio/bufio.go
var (
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count")
)
error 的内容是为了方便调试或者日志记录,而非方便程序控制
仅使用错误内容判断错误,而进行错误逻辑处理可能造成隐患,因为可能存在相同错误内容,但不同错误对象的情况。errors.New()
时也会返回当前对象的指针,以确保每个新建一个对象都是唯一的。
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
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbfuhipu-1655983351596)(/Users/zsl/Library/Application Support/typora-user-images/image-20220623190257837.png)]
Sentinel Error 通常被用作底层工具开发使用,第三方包如果大量使用可能会导致循环导入问题(import loop)
需要放置在公共包中,且需要有文档描述。(注意:阅读源码发现方法的返回值仍然是 error
)
// ErrShortWrite means that a write accepted fewer bytes than requested (写入的字节数少于请求的)
// but failed to return an explicit error.
var ErrShortWrite = errors.New("short write")
// errInvalidWrite means that a write returned an impossible count. (写入的返回是无法计数的)
var errInvalidWrite = errors.New("invalid write result")
// ErrShortBuffer means that a read required a longer buffer than was provided.(读取的字节数大于提供的)
var ErrShortBuffer = errors.New("short buffer")
......
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
// underlyingError returns the underlying error for known os error types.
func underlyingError(err error) error {
switch err := err.(type) {
case *PathError:
return err.Err
case *LinkError:
return err.Err
case *SyscallError:
return err.Err
}
return err
}
将错误向上抛,在某个地方统一处理
fmt.Errorf()
做转换使用
github.com/pkg/errors
做错误包装,
errors.New()
或者 errors.Errorf()
返回错误error.Warp()
或者error.Warpf()
保存堆栈信息(适用于标准库)%+v
方式打印错误堆栈信息errors.Cause
打印原始错误,可以使用预定义错误、类型断言、行为判定等配合使用indented flow is for errors
无错误的代码正常执行,而不是缩进的代码(推荐判断
err != nil
不推荐err == nil
)
func test() {
a, err := funca()
if err != nil { //推荐
//do error handling
}
//do something
if err == nil { //不推荐
//do something
}
// do error handling
}
eliminate error handling by eliminate errors
避免无效的错误判断
func test() error { //不推荐
err := funca()
if err != nil {
return err;
}
return nil;
}
func test() error { //推荐
return funca()
}
可以通过错误记录或者收集的方式减少错误判断
//go/1.17.5/libexec/src/bufio/scan.go
// advance consumes n bytes of the buffer. It reports whether the advance was legal.
func (s *Scanner) advance(n int) bool {
if n < 0 {
s.setErr(ErrNegativeAdvance)
return false
}
if n > s.end-s.start {
s.setErr(ErrAdvanceTooFar)
return false
}
s.start += n
return true
}
// setErr records the first error encountered.
func (s *Scanner) setErr(err error) {
if s.err == nil || s.err == io.EOF {
s.err = err
}
}
未使用错误收集方式
type Header struct {
Key, Value string
}
type Status struct {
Code int
Reason string
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
_, err := fmt.Fprintf(w, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
if err != nil {
return err
}
for _, h := range headers {
_, err := fmt.Fprintf(w, "%s: %s\r\n", h.Key, h.Value)
if err != nil {
return err
}
}
if _, err := fmt.Fprint(w, "\r\n"); err != nil {
return err
}
_, err = io.Copy(w, body)
return err
}
使用错误收集方式
type errWriter struct {
io.Writer
err error
}
func (e *errWriter) Write(buf []byte) (int, error) {
if e.err != nil {
return 0, e.err
}
var n int
n, e.err = e.Writer.Write(buf)
return n, nil
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
ew := &errWriter{Writer: w}
fmt.Fprintf(ew, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
for _, h := range headers {
fmt.Fprintf(ew, "%s: %s\r\n", h.Key, h.Value)
}
fmt.Fprint(ew, "\r\n")
io.Copy(ew, body)
return ew.err
}