这是我纯手写的《Go语言入门》,手把手教你入门Go。源码+文章,看了你就会,此处应有掌声!
文章中所有的代码我都放到了github.com/GanZhiXiong/go_learning这个仓库中。在看文章的时候,对照仓库中代码学习效果更佳!
在之前的文章中经常有写到类似下面的代码:
if err != nil {
// do something...
}
那么什么是nil呢?
nil是预定义的标识符,代表指针、通道、函数、接口、映射或切片的零值,也就是预定义好的一个变量。
type Type int
var nil Type //是预定义好的变量
因此nil不是Go的关键字,所以你可以定义一个变量的名称为nil
,你甚至可以改变nil的值。
var nil string
nil = "hi"
t.Log(nil)
在Go中,布尔类型的零值(默认值)为false
,数值类型的零值为0
,字符串类型的零值为空字符串
。
而以下类型的零值为nil
,且nil只能赋值给以下类型:
func TestNilCompare(t *testing.T) {
var arr []int
var arr1 []int
var num *int
var num1 *int
t.Log(arr == nil)
t.Log(arr1 == nil)
t.Log(num == nil)
t.Log(num1 == nil)
// 相同类型的nil值可能无法比较
t.Log(num == num1)
// Invalid operation: arr == arr1 (operator == is not defined on []int)
//t.Log(arr == arr1)
// 不同类型的nil值不能比较
// Invalid operation: arr == num (mismatched types []int and *int)
//t.Log(arr == num)
t.Log(unsafe.Pointer(&arr))
t.Log(&num)
t.Logf("%p\n", arr)
t.Logf("%p", num)
}
=== RUN TestNilCompare
20_interface_nil_test.go:57: true
20_interface_nil_test.go:58: true
20_interface_nil_test.go:59: true
20_interface_nil_test.go:60: true
20_interface_nil_test.go:63: true
20_interface_nil_test.go:71: 0xc0000b4080
20_interface_nil_test.go:72: 0xc0000a0028
20_interface_nil_test.go:73: 0x0
20_interface_nil_test.go:74: 0x0
--- PASS: TestNilCompare (0.00s)
PASS
接口在底层的实现由两个部分:type
(类型)和 data
(值)。
type是存储变量的动态类型;
data是存储变量的具体数据。
func TestInterfaceNil1(t *testing.T) {
// val的底层结构应该是(int64, 1)
var val interface{} = int64(1)
t.Log(reflect.TypeOf(val))
// 因为字面量的整数在Go中默认类型为int,所以val的底层结构变成了(int, 2)
val = 2
t.Log(reflect.TypeOf(val))
}
=== RUN TestInterfaceNil1
20_interface_nil_test.go:46: int64
20_interface_nil_test.go:49: int
--- PASS: TestInterfaceNil1 (0.00s)
PASS
interface是否nil,分为以下两种情况:
因此这是一个坑啊,在其他编程语言直接判断是否等于nil即可,但是Go中不能这样做。
下面用一个简单的示例来说明下:
func TestInterfaceNil(t *testing.T) {
var a interface{}
t.Log(a)
t.Log(a == nil) // true
a = nil
t.Log(a)
t.Log(a == nil) // true
// cannot convert nil to type int
//var b interface{} = (int)(nil)
var b interface{} = (*int)(nil)
t.Log(b)
t.Log(b == nil) // false
b = 1
t.Log(b == nil) // false
t.Log()
// 接口指针类型变量,底层结构为{*interface{}, nil},所以不为nil
var c interface{} = (*interface{})(nil)
c = 123
t.Log(c)
t.Log(c == nil) // false
var d = (*interface{})(nil)
// Cannot use '2' (type untyped int) as type *interface{}
//d = 2
// 能编译通过,但是会panic,这是因为d指向的是一个无效的内存地址
//*d = 2
t.Log(d)
t.Log(d == nil) // true
}
一个常见的场景就是error接口类型的值与nil比较。
请看代码:
type data struct{}
func (d *data) Error() string { return "" }
// 这样做不严谨,因为返回的指针p被包装成error类型,所以返回的底层结构为(*data, nil)
// 返回值nil相比则不相等,这不是预期的结果。
func test() error {
var p *data
return p
}
// 因此得用下面这种方式,如果出错了,返回p;没出错,则返回nil。
func testRight() error {
var p *data
isTestError := false
if isTestError {
return p
}
return nil
}
func TestNilError(t *testing.T) {
var err error = test()
if err == nil {
t.Log("err is nil, success")
} else {
t.Log("err is not nil, error")
}
err = testRight()
if err == nil {
t.Log("err is nil, success")
} else {
t.Log("err is not nil, error")
}
}
利用反射来判断:
func IsNil(value interface{}) bool {
if value == nil {
return true
}
var rv reflect.Value
if v, ok := value.(reflect.Value); ok {
rv = v
} else {
rv = reflect.ValueOf(value)
}
switch rv.Kind() {
case reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Ptr,
reflect.Interface,
reflect.UnsafePointer:
return rv.IsNil()
}
return false
}
func TestInterfaceNilCompare(t *testing.T) {
var s fmt.Stringer
t.Log(s, IsNil(s))
var i interface{}
t.Log(i, IsNil(i))
g := GetStringer()
t.Log(g, IsNil(g))
test := test()
t.Log(test, IsNil(test))
test1 := testRight()
t.Log(test1, IsNil(test1))
}