Go语言的函数能够一次返回多个结果。这就为咱们温和地报告错误提供了语言级别的支持。实际上,这也是Go语言中处理错误的惯用法之一。咱们先来回顾前一小节的例子:函数
func readFile(path string) ([]byte, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() return ioutil.ReadAll(file) }
函数readFile
有两个结果声明。第二个结果声明的类型是error
。error
是Go语言内置的一个接口类型。它的声明是这样的:编码
type error interface { Error() string }
显然,只要一个类型的方法集合包含了名为Error
、无参数声明且仅声明了一个string
类型的结果的方法,就至关于实现了error
接口。os.Open
函数的第二个结果值就的类型就是这样的。咱们把它赋给了变量err
。也许你已经意识到,在Go语言中,函数与其调用方之间温和地传递错误的方法便是如此。
在调用了os.Open
函数并取得其结果以后,咱们判断err
是否为nil
。若是答案是确定的,那么就直接把该错误(这里由err
表明)返回给调用方。这条if
语句其实是一条卫述语句。这样的语句会检查流程中的某个步骤是否存在异常,并在必要时停止流程并报告给上层的程序(这里是调用方)。在Go语言的标准库以及不少第三方库中,咱们常常能够看到这样的代码。咱们也建议你们在本身的程序中善用这样的卫述语句。
如今咱们把目光聚焦到readFile
函数中的最后一条语句上。这是一条return
语句。它把对ioutil.ReadAll
函数的调用的结果直接做为readFile
函数的结果返回了。实际上,ioutil.ReadAll
函数的结果声明列表与readFile
的结果声明列表是一致的。也就是说,它们声明的结果的数量、类型和顺序都是相同的。所以,咱们才可以作这种返回结果上的“嫁接”。这又是一个Go语言编码中的惯用法。
好了,在知晓怎样在传递错误以后,让咱们来看看怎样创造错误。没错,在不少时候,咱们须要创造出错误(即error
类型的值)并把它传递给上层程序。这很简单。只需调用标准库代码包errors
的New
函数便可。例如,咱们只要在readFile
函数的开始处加入下面这段代码就能够更快的在参数值无效时告知调用方:spa
if path == "" { return nil, errors.New("The parameter is invalid!") }
errors.New
是一个很经常使用的函数。在Go语言标准库的代码包中有不少由此函数建立出来的错误值,好比os.ErrPermission
、io.EOF
等变量的值。咱们能够很方便地用操做符==
来判断一个error
类型的值与这些变量的值是否相等,从而来肯定错误的具体类别。就拿io.EOF
来讲,它表明了一个信号。该信号用于通知数据读取方已无更多数据可读。咱们在获得这样一个错误的时候不该该把它当作一个真正的错误,而应该只去结束相应的读取操做。请看下面的示例:code
br := bufio.NewReader(file) var buf bytes.Buffer for { ba, isPrefix, err := br.ReadLine() if err != nil { if err == io.EOF { break } fmt.Printf("Error: %s\n", err) break } buf.Write(ba) if !isPrefix { buf.WriteByte('\n') } }
能够看到,这段代码使用到了前面示例中的变量file
。它的功能是把file
表明的文件中的全部内容都读取到一个缓冲器(由变量buf
表明)中。请注意,该示例中的第6~8行代码。若是断定err
表明的错误值等于io.EOF
的值(即它们是同一个值),那么咱们只需退出当前的循环以使读取操做结束便可。
总之,只要可以善用error
接口、errors.New
函数和比较操做符==
,咱们就能够玩儿转Go语言中的通常错误处理。接口