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
本文出至:学新通