
type error interface {
Error() string
}
var EOF = errors.New("EOF")
var ErrUnexpectedEOF = errors.New("unexpected EOF")
var ErrShortBuffer = errors.New("short buffer")
// os.PathError
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
// An Error represents a network error.
type Error interface {
error
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}
type AddrError struct {
Err string
Addr string
}
func (e *AddrError) Error() string {
if e == nil {
return ""
}
s := e.Err
if e.Addr != "" {
s = "address " + e.Addr + ": " + s
}
return s
}
func (e *AddrError) Timeout() bool { return false }
func (e *AddrError) Temporary() bool { return false }
func main() {
_,err := ReadConfig()
if err != nil {
//org err: *os.PathError open /Users/js/.setting.xml: no such file or directory
fmt.Printf("org err: %T %vn",errors.Cause(err),errors.Cause(err))
// wrap err: could not read config: open fail: open /Users/js/.setting.xml: no such file or directory
fmt.Printf("wrap err: %vn",err)
// 详细堆栈信息
fmt.Printf("stack trace: n %+vn",err)
}
}
func ReadFile(path string)([]byte,error) {
f,err := os.Open(path)
if err != nil {
// 只对err进行包装,不破坏原错误,携带了附加的错误信息&堆栈信息
return nil, errors.Wrap(err,"open fail")
}
defer f.Close()
return nil, err
}
func ReadConfig() ([]byte, error) {
home := os.Getenv("HOME")
config,err:=ReadFile(filepath.Join(home,".setting.xml"))
// 这里也只直接进行包装
return config,errors.WithMessage(err,"could not read config")
}
func CountLines(r io.Reader)(int,error) {
var (
br = bufio.NewReader(r)
lines = 0
err error
)
for {
_,err = br.ReadString('n')//读取一行
lines++
if err != nil {//遇到错误就退出
break
}
}
if err != io.EOF {//如果不是eof错误表面读取出错
return 0,nil
}
return lines,nil
}
其中出现了两次错误处理,是因为Reader.ReadString返回的是一个具体的错误,导致调用者需要去处理错误。而改进后的版本:func CountLines2(r io.Reader)(int,error) {
sc := bufio.NewScanner(r)
lines := 0
for sc.Scan() {
lines++
}
return lines, sc.Err()
}
其中Scanner.Scan的方法直接屏蔽了具体错误,只返回行为结构:即scan失败时(包括eof和err)返回false,而Scanner.Err也屏蔽了EOF错误,所以使得此处调用点能显得简洁。对比scanner和reader就是我们设计使应该考虑的,如果上层强依赖结果,可能需要我们返回具体值,反之我们就应该设计得更简洁,降低耦合func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
_, err := fmt.Fprintf(w, "http/1.1 %d %srn", st.Code, st.Reason)
if err != nil {
return err
}
for _, h := range headers {
_, err := fmt.Fprintf(w, "%s:%srn", h.Key, h.Value)
if err != nil {
return err
}
}
if _, err := fmt.Fprintf(w, "rn"); err != nil {
return err
}
_, err = io.Copy(w, body)
return err
}
其中因为要对w进行多次写,调用的是Fprintf这种写方法会返回err,必须要去处理,所以导致一堆err!=nil的处理,这种似乎没有什么好的方法去解决,你可能会说调用一个不会返回错误的方法不就可以了,但如果没有呢,rob pike(go创始人)提供了一种思路:func WriteResponse2(w io.Writer, st Status, headers []Header, body io.Reader) error {
ew := &errWrite{Writer:w}
fmt.Fprintf(ew, "http/1.1 %d %srn", st.Code, st.Reason)
for _, h := range headers {
fmt.Fprintf(ew, "%s:%srn", h.Key, h.Value)
}
fmt.Fprintf(ew, "rn")
io.Copy(ew, body)
return ew.err
}
type errWrite struct {
io.Writer
err error
}
func (e *errWrite) Write(buf []byte)(int,error) {
if e.err != nil {
return 0,e.err
}
n := 0
n,e.err = e.Writer.Write(buf)
return n,nil
}
其中for循环部分原来可以直接retrun的逻辑,会变成最后才return,但如果headers数组小(1万次循环才1毫秒不到),可以说都是小问题。在代码层面上,如果我们errWrite的调用点比较多,那代码的简洁度上的优化是巨大的。