Go起步:9、Go的错误处理--自定义异常

Go的错误处理方式

Go语言主要的设计准则是:简洁、明白。
简洁是指语法和C类似,相当的简单;明白是指任何语句都是很明显的,不含有任何隐含的东西,在错误处理方案的设计中也贯彻了这一思想。

错误处理是学习任何编程语言都需要考虑的一个重要话题。以前学习C语言时,对于错误的处理就是通过返回-1或者NULL之类的信息来表示错误,但是对于使用者来说,不查看相应的API说明文档,根本搞不清楚这个返回值究竟代表什么意思,比如:返回0是成功还是失败。后来,许多编程语言会在语言层面上增加错误处理的支持,比如java的异常(exception)的概念和try-catch关键字的引入。
Go的简洁性有一点常被人说道,就是Go的25个关键字。所以Go在错误处理上没有增加关键字,而是定义了一个叫做error的类型,来显式表达错误。在使用时,通过把返回的error变量与nil的比较,来判定操作是否成功。而且Go的多值返回可以让我们在返回一个常规的返回值之外,还能轻易地返回一个详细的错误描述。

Error

Go标准库error

上面讲的Go对于错误处理的方式,在Go的许多类库中都可以发现。这里看下下面的例子。

package main

import (
    "fmt"
    "strconv"
)

func main() {

    i, err := strconv.Atoi("42")
    if err != nil {
        fmt.Printf("couldn't convert number: %v\n", err)
        return
    }
    fmt.Println("Converted integer:", i)
}

strconv.Atoi是一个将字符串转为int的方法。上面的代码直接执行结果如下:
Go起步:9、Go的错误处理--自定义异常_第1张图片
如果将代码改成:

i, err := strconv.Atoi("abc")

Go起步:9、Go的错误处理--自定义异常_第2张图片
看下Atoi函数的定义,在strconv/atoi.go源文件下:

// Atoi is shorthand for ParseInt(s, 10, 0).
func Atoi(s string) (i int, err error) {
    i64, err := ParseInt(s, 10, 0)
    return int(i64), err
}

可以看出,这里就是返回一个常规的返回值之外,多返回了一个详细的错误描述。
这里的error是Go的一个内置的接口类型,我们可以在$GOROOT\src\builtin下面找到相应的定义。

type error interface {     
    Error() string 
}

而我们在很多内部包里面用到的 error是errors包(位置$GOROOT\src\errors)里实现的私有结构errorString.

// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// 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()方法。让我们可以通过errors.New把一个字符串转化为errorString,以得到一个满足接口error的对象。至于New()方法如何使用,将上面的例子稍微做下修改如下:

package main

import (
    "errors"
    "fmt"
    "strconv"
)

func main() {

    i, err := strconv.Atoi("abc")
    if err != nil {
        //使用New()显示错误信息
        fmt.Println(errors.New("couldn't convert number"))
        return
    }
    fmt.Println("Converted integer:", i)
}

这里写图片描述

自定义error

通过上面的介绍我们知道error是一个interface,所以通过定义实现此接口的结构,我们就可以实现自己的错误定义。
Go接口的概念和其他变成语言差不多,不过更具灵活性。使用时不需要从接口继承或者像Java一样需要使用implements来明确指定类型和接口之间的关系。
下面代码实现了一个自定义的异常.

package main

import (
    "fmt"
)

// 定义一个 DivideError 结构
type DivideError struct {
    dividee int
    divider int
}

type error interface {
    Error() string
}

// 实现   `error` 接口
func (de *DivideError) Error() string {
    strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
    return fmt.Sprintf(strFormat, de.dividee)
}

// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
    if varDivider == 0 {
        dData := DivideError{
            dividee: varDividee,
            divider: varDivider,
        }
        errorMsg = dData.Error()
        return
    } else {
        return varDividee / varDivider, ""
    }

}

func main() {

    // 正常情况
    if result, errorMsg := Divide(100, 10); errorMsg == "" {
        fmt.Println("100/10 = ", result)
    }
    // 当被除数为零的时候会返回错误信息
    if _, errorMsg := Divide(100, 0); errorMsg != "" {
        fmt.Println("errorMsg is: ", errorMsg)
    }

}

Go起步:9、Go的错误处理--自定义异常_第3张图片

以上只是异常的使用方式,可是对于异常的处理要贯穿到程序的整个设计和开发过程,知道了方法,还得不断培养意识才可以.
还有一点,这里引入了Go的接口的概念,一开始我也是有些不明白,因为Go的接口和像java的面向对象的里的接口含义区别还是蛮大的。留在后面去探索了。

你可能感兴趣的:(Golang,go,go语言)