现象
Gonil ≠ nil

先来看个例子:

type CustomizedError struct {
	ErrorCode int
	Msg       string
}

func (e *CustomizedError) Error() string {
	return fmt.Sprintf("err code: %d, msg: %s", e.ErrorCode, e.Msg)
}
func main() {
	txn, err := startTx()
	if err != nil {
		log.Fatalf("err starting tx: %v", err)
	}

	if err = txn.doUpdate(); err != nil {
		log.Fatalf("err updating: %v", err)
	}

	if err = txn.commit(); err != nil {
		log.Fatalf("err committing: %v", err)
	}
	fmt.Println("success!")
}

type tx struct{}

func startTx() (*tx, error) {
	return &tx{}, nil
}

func (*tx) doUpdate() *CustomizedError {
	return nil
}

func (*tx) commit() error {
	return nil
}

这是一个简化过了的例子,在上述代码中,我们创建了一个事务,然后做了一些更新,在更新过程中如果发生了错误,希望返回对应的错误码和提示信息。

如果感兴趣的话,可以在这个地址在线运行这份代码:

nilsuccess
err updating: <nil>
寻找原因
nilerr ≠ nilnil
errorerrorinterfaceError()
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
	Error() string
}
type Tvalue V

来看一个简单的例子:

var it interface{}
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // <nil> <invalid reflect.Value>
it = 1
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // int 1
it = "hello"
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // string hello
var s *string
it = s
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // *string <nil>
ss := "hello"
it = &ss
fmt.Println(reflect.TypeOf(it), reflect.ValueOf(it)) // *string 0xc000096560
interfaceTVnil
nilV=nilT*string
nilinterfacenil
errTV
func main() {
	txn, err := startTx()
	fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))
	if err != nil {
		log.Fatalf("err starting tx: %v", err)
	}

	if err = txn.doUpdate(); err != nil {
		fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))
		log.Fatalf("err updating: %v", err)
	}

	if err = txn.commit(); err != nil {
		log.Fatalf("err committing: %v", err)
	}
	fmt.Println("success!")
}

输出如下:

<nil> <invalid reflect.Value>
*err.CustomizedError <nil>
errstartTxerrornilTVnil
txn, err := startTx()
fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // <nil> <invalid reflect.Value>

func startTx() (*tx, error) {
	return &tx{}, nil
}
doUpdate*CustomizedErrornil*CustomizedErrornil
err = txn.doUpdate()
fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // *err.CustomizedError <nil>
err ≠ nilerrTnilTVnilniltrue
doUpdateerr
if err := txn.doUpdate(); err != nil {
		log.Fatalf("err updating: %v", err)
}
errinterface*CustomizedErrornilerr ≠ nilfalse

问题到这里似乎就告一段落了,但,再仔细想想,就会发现这其中似乎还是漏掉了一环。

interfaceTVnilnil
interfaceinterfaceifaceeface
type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type eface struct {
	_type *_type
	data  unsafe.Pointer
}
tabdataiface
itab
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs.
type itab struct {
	inter *interfacetype
	_type *_type
	hash  uint32 // copy of _type.hash. Used for type switches.
	_     [4]byte // 用于内存对齐
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
itabinnerinterface_typeinterfaceinterface
itab_typeifacedatainterfaceTV_typedatareflect.TypeOfreflect.ValueOf
hash_typehash
fun
interfacetype
type interfacetype struct {
	typ     _type
	pkgpath name
	mhdr    []imethod
}
_typeinterface
interfaceinterface

了解了这些之后,我们再来看一下之前的例子:

txn, err := startTx()
erritab.inter.typerroritab._typenil
err = txn.doUpdate()
errerritab._type*CustomizedErrorerritab.inter.typerror*CustomizedErrornil
nilvaluenilnilitab._typenil
errifaceifaceunsafe.Pointer
func TestErr(t *testing.T) {
	txn, err := startTx()
	fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))
	if err != nil {
		log.Fatalf("err starting tx: %v", err)
	}

	p := (*iface)(unsafe.Pointer(&err))
	fmt.Println(p.data)

	if err = txn.doUpdate(); err != nil {
		fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err))
		p := (*iface)(unsafe.Pointer(&err))
		fmt.Println(p.data)
		log.Fatalf("err updating: %v", err)
	}

	if err = txn.commit(); err != nil {
		log.Fatalf("err committing: %v", err)
	}
	fmt.Println("success!")
}
inter.typ.kindruntime
const (
	kindBool = 1   iota
	kindInt
	kindInt8
	kindInt16
	kindInt32
	kindInt64
	kindUint
	kindUint8
	kindUint16
	kindUint32
	kindUint64
	kindUintptr
	kindFloat32
	kindFloat64
	kindComplex64
	kindComplex128
	kindArray
	kindChan
	kindFunc
	kindInterface
	kindMap
	kindPtr
	kindSlice
	kindString
	kindStruct
	kindUnsafePointer

	kindDirectIface = 1 << 5
	kindGCProg      = 1 << 6
	kindMask        = (1 << 5) - 1
)
kind = 20kindInterface
总结
ifaceefaceifacei_typeitab._typenilnil

本文出至:学新通