golang error和nil深入分析

作者:袖梨 2022-06-25

go处理错误的方式非常原始和有效,go是通过function返回error类型而非主流语言抛出异常。在golang里永远不要忽略错误,否则可能导致程序的意外崩溃。
通常用如下方式检测错误:
if value, err := pack1.Func1(param1); err != nil {
    fmt.Printf(“Error %s in pack1.Func1 with parameter %v”, err.Error(), param1)
    return    // or: return err
}
// Process(value)

从上可以看出如果err不为nil表明存在错误。那么error和nil是啥?
error是golang的预定义接口:
type error interface {
 Error() string
}

那么如何定义error?
package main
 
import (
 "fmt"
 "errors"
)
 
type myError struct{}
 
func (this *myError) Error() string { return "" }
 
func main()  {
 //方法一:采用errors包的New方法 返回一个err的类型
 var err1 error = errors.New("this is an error")
 fmt.Println(err1.Error())
 
 //方法二:采用fmt.Errof 将string信息转化为error信息 并返回
 err2 := fmt.Errorf("%s", "the error test for fmt.Errorf")
 fmt.Println(err2.Error())
 
 //方法三:自定义error,下面细说
}

我们已经知道golang的error是一个interface,go中interface是作为两个成员实现:一个类型和一个值。该值被称为接口的动态值, 它是一个任意的具体值,而该接口的类型则为该值的类型。对于 int 值3, 一个接口值示意性地包含(int, 3)。
只有在内部值和类型都未设置时(nil, nil),一个接口的值才为 nil。特别是,一个 nil 接口将总是拥有一个 nil 类型。若我们在一个接口值中存储一个 int 类型的指针,则内部类型将为 int,无论该指针的值是什么,这样的接口值会是非 nil 的,即使在该指针的内部值为 nil,形如(*int, nil)。
下面举个简单的例子:
package main
 
import (
 "math"
 "fmt"
 "reflect"
)
 
// 定义接口 Abser
type Abser interface {
 Abs() float64
}
 
// 定义结构体 Vertex
type Vertex struct {
 X, Y float64
}
 
// 实现方法 Abs
func (v *Vertex) Abs() float64 {
 return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
func main() {
 v := Vertex{3, 4}
 // 成功,能够持有 *Vertex 类型的值
 var a Abser = &v
 
 fmt.Println(reflect.TypeOf(v)) //main.Vertex
 fmt.Println(reflect.TypeOf(a)) //*main.Vertex
 
 // 出错,不能持有 Vertex 类型的值
 // 因为在 *Vertex 上定义了方法 Abs,而未在 Vertex 上定义
 //var b Abser = v
}

那么go在自定义error的时候就要注意了:
package main
 
import (
 "errors"
 "fmt"
 "reflect"
)
 
type myError struct{}
 
func (this *myError) Error() string { return "" }
 
func bad() bool {
 return true
}
 
//自定义错误返回函数
func test() error {
 var p *myError = nil
 if bad() {
  return p
 }
 return nil
}
 
//只是返回错误非空
func test1() error {
 var val error = errors.New("val")
 return val
 
}
 
func main() {
 var e error = test()
 if e == nil {
  fmt.Println("e is nil")
 } else {
  fmt.Println(reflect.TypeOf(e))
  fmt.Println(reflect.ValueOf(e))
  fmt.Println("e is not nil")
 }
 
 var e1 error = test1()
 if e1 == nil {
  fmt.Println("e1 is nil")
 } else {
  fmt.Println("e1 is not nil")
  fmt.Println(e1.Error())
 }
 
 /*输出如下:
 *main.myError
 
 e is not nil
 e1 is not nil
 val
  */
}

再来说说nil:
golang的nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。nil是预先说明的标识符,也即通常意义上的关键字。在golang中,nil只能赋值给指针、channel、func、interface、map或slice类型的变量。如果未遵循这个规则,则会引发panic。

相关文章

精彩推荐