分享
  1. 首页
  2. 文章

Go-Errors

u013344915 · · 3154 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

error数据类型

在前面的例子中,已经大量地用到了error这个数据类型。而且虽然未加说明,已经能够蒙着用这个数据类型。——编程经常就是凭着感觉蒙着干活,而且这代码还能够很好地工作。最后再查阅资料,找到佐证,心理上再得到一番安慰/踏实。

严格来讲,与其说error类型,不如说error接口。其定义如下:

type error interface {
 Error() string 
}

既然是接口,那么在使用的时候,遇到的都是error的实现。接下来,以打开文件为例,对error展开说明。

os.Open()

godoc

D:\examples>godoc cmd/os Open
type File struct {
 // contains filtered or unexported fields
}
 File represents an open file descriptor.
func Open(name string) (*File, error)
 Open opens the named file for reading. If successful, methods on the
 returned file can be used for reading; the associated file descriptor
 has mode O_RDONLY. If there is an error, it will be of type *PathError.

PathError

如同godoc所述,当Open出错的时候,对应的是PathError类型,其为error接口的一个实现。代码如下(src\os\error.go):

// 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() }

这里PathError struct实现了error接口的func Error() string方法,所以说PathError是error的一个实现。PathError中,有一个Err error成员,自然地,这又是某个error实现。

Open()实现代码

Open() - src/os/file.go

// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(name string) (*File, error) {
 return OpenFile(name, O_RDONLY, 0)
}

OpenFile() - src/os/file_windows.go

// OpenFile is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
 if name == "" {
 return nil, &PathError{"open", name, syscall.ENOENT}
 }
 r, errf := openFile(name, flag, perm)
 if errf == nil {
 return r, nil
 }
 r, errd := openDir(name)
 if errd == nil {
 if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
 r.Close()
 return nil, &PathError{"open", name, syscall.EISDIR}
 }
 return r, nil
 }
 return nil, &PathError{"open", name, errf}
}

可以看到OpenFile()返回的的确是PathError类型。注意到return语句中的&。这在Go-interface中有说明。

syscall.EXXXXX - src/syscall/errors_plan9.go

// Errors
var (
 EINVAL = NewError("bad arg in system call")
 ENOTDIR = NewError("not a directory")
 EISDIR = NewError("file is a directory")
 ENOENT = NewError("file does not exist")
 EEXIST = NewError("file already exists")
 EMFILE = NewError("no free file descriptors")
 EIO = NewError("i/o error")
 ENAMETOOLONG = NewError("file name too long")
 EINTR = NewError("interrupted")
 EPERM = NewError("permission denied")
 EBUSY = NewError("no free devices")
 ETIMEDOUT = NewError("connection timed out")
 EPLAN9 = NewError("not supported by plan 9")
 // The following errors do not correspond to any
 // Plan 9 system messages. Invented to support
 // what package os and others expect.
 EACCES = NewError("access permission denied")
 EAFNOSUPPORT = NewError("address family not supported by protocol")
)

这里给出的是PathError的err Error成员,的确也是一个error实现。——当然,需要进一步确定NewError()。这又是什么鬼?

NewError() - src/syscall/syscall_plan9.go

NewError()是一个函数,把一个字符串转换成为一个ErrorString对象。

// NewError converts s to an ErrorString, which satisfies the Error interface.
func NewError(s string) error { return ErrorString(s) }

ErrorString() - src/syscall/syscall_plan9.go

ErrorString既是string,又是error对象。——这里的type类似于C/C++的typedef。

// ErrorString implements Error's String method by returning itself.
type ErrorString string
func (e ErrorString) Error() string { return string(e) }

小结

现在转了一大圈,就是把PathError以及PathError中的err对象全部过了一遍。结论就是PathError是error实现,ErrorString是error实现。——其实整个Go中,有大量的error实现。但只限于注意其只有一个func Error() string方法即可,其返还error的详细信息。

fmt.Println(err)

示例代码

package main
import (
 "fmt"
 "os"
)
func main() {
 file, err := os.Open("abcdefg.xyz")
 if err != nil {
 fmt.Println("err: ", err)
 fmt.Println("Error():", err.Error())
 } else {
 fmt.Println("Open OK.")
 file.Close()
 }
}

输出结果

D:\examples>go run helloworld.go
err: open abcdefg.xyz: The system cannot find the file specified.
Error(): open abcdefg.xyz: The system cannot find the file specified.
D:\examples>

Println()的入参

注意到Println()的两种入参:

fmt.Println("err: ", err)
fmt.Println("Error():", err.Error())

打印结果是一样的。

难不成Go也有Java一样的toString()方法?事实上,Go比Java更加智能,关于这一块,不再展开(目前还没有分析透彻),只给出src/fmt/print.go的如下代码。简单说,就是会利用反射机制,自动调用error接口的Error()方法,打印这个方法的返回值。所以,以上两种入参的效果是一样的。当然,后者的性能更优。

func (p *pp) printArg(arg interface{}, verb rune) {
 //...
 // Some types can be done without reflection.
 switch f := arg.(type) {
 case bool:
 p.fmtBool(f, verb)
 //...
 case string:
 p.fmtString(f, verb)
 case []byte:
 p.fmtBytes(f, verb, "[]byte")
 case reflect.Value:
 p.printValue(f, verb, 0)
 default:
 // If the type is not simple, it might have methods.
 if !p.handleMethods(verb) {
 // Need to use reflection, since the type had no
 // interface methods that could be used for formatting.
 p.printValue(reflect.ValueOf(f), verb, 0)
 }
 }
}

实用函数

上面描述的内容似乎和日常编码没有多大关系,接下来聊聊一些实用方面。

package errors

源代码

这个包的内容很少,直接拷贝过来(省略掉版权信息):

// Package errors implements functions to manipulate errors.
package errors
// New returns an error that formats as the given text.
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
}

也就是说,errors这个包只有一个对外可见的函数,即New(),其返还一个实现了error接口的对象(即errorString实现)。

示例代码

package main
import (
 "fmt"
 "errors"
)
func myadd(x, y int) (ret int, err error) {
 if x <= 0 || y <= 0 {
 err = errors.New("x or y less or equal 0!")
 return
 } else {
 ret = x + y
 err = nil
 return
 }
}
func test(x, y int) {
 ret, err := myadd(x, y)
 if err != nil {
 fmt.Println(err.Error())
 } else {
 fmt.Println(ret)
 }
}
func main() {
 test(0, 1)
 test(1, 0)
 test(-1, 1)
 test(1, -1)
 test(1, 1)
}

运行结果:

D:\examples>go run helloworld.go
x or y less or equal 0!
x or y less or equal 0!
x or y less or equal 0!
x or y less or equal 0!
2
D:\examples>

fmt.Errorf()

这是更加实用的一个函数。

源代码 - src/fmt/print.go

// Sprintf formats according to a format specifier and returns the resulting string.
func Sprintf(format string, a ...interface{}) string {
 p := newPrinter()
 p.doPrintf(format, a)
 s := string(p.buf)
 p.free()
 return s
}
// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
func Errorf(format string, a ...interface{}) error {
 return errors.New(Sprintf(format, a...))
}

示例

只需要把上例myadd中的一句话替换为://哦,还要注释掉errors的import。

err = fmt.Errorf("x(%d) or y(%d) less or equal 0!", x, y)

运行结果:

D:\examples>go run helloworld.go
x(0) or y(1) less or equal 0!
x(1) or y(0) less or equal 0!
x(-1) or y(1) less or equal 0!
x(1) or y(-1) less or equal 0!
2
D:\examples>

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:u013344915

查看原文:Go-Errors

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3154 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏