起因在这:反射中CanInterface,CanAddress,CanSet的区别
golang1.13.1对应源码如下(不想康直接略过)
// CanInterface reports whether Interface can be used without panicking.
func (v Value) CanInterface() bool {
if v.flag == 0 {
panic(&ValueError{"reflect.Value.CanInterface", Invalid})
}
return v.flag&flagRO == 0
}
// CanAddr reports whether the value's address can be obtained with Addr.
// Such values are called addressable. A value is addressable if it is
// an element of a slice, an element of an addressable array,
// a field of an addressable struct, or the result of dereferencing a pointer.
// If CanAddr returns false, calling Addr will panic.
func (v Value) CanAddr() bool {
return v.flag&flagAddr != 0
}
// CanSet reports whether the value of v can be changed.
// A Value can be changed only if it is addressable and was not
// obtained by the use of unexported struct fields.
// If CanSet returns false, calling Set or any type-specific
// setter (e.g., SetBool, SetInt) will panic.
func (v Value) CanSet() bool {
return v.flag&(flagAddr|flagRO) == flagAddr
}
三个函数都和v.flag有关,flag定义如下
type flag uintptr
flag的生成过程如下:
// func unpackEface(i interface{}) Value
f := flag(t.Kind()) //给flag添加类型字段(0-4位)
if ifaceIndir(t) {
f |= flagIndir // flagIndir=1 << 7 第7位置1
}
// func (t *rtype) Kind() Kind
return Kind(t.kind & kindMask)
// const define
type Kind uint
// func ifaceIndir(t *rtype) bool
return t.kind&kindDirectIface == 0
源码可见采用了bit map(32bit),各个位作用如下:
| 标志名 | Kind Mask | Sticky RO | Embed RO | Indir | Addr | Method | MethodNum |
|---|---|---|---|---|---|---|---|
| 值 | 1<<flagKindWidth - 1 | 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 9 | flag<<MethodShift |
| 位置 | 第0-4位 | 第5位 | 第6位 | 第7位 | 第8位 | 第9位 | 第10-31位(共22位 注释给出的是23+) |
| 注释 | five bits give the Kind of the value | obtained via unexported not embedded field, so read-only | obtained via unexported embedded field, so read-only | val holds a pointer to the data | v.CanAddr is true (implies flagIndir) | v is a method value,If flag.kind() != Func, code can assume that flagMethod is unset | a method number for method values |
| 用处 | 类型值 | 粘性只读?通过私有未嵌入变量获取 | 嵌入只读?通过非私有未嵌入变量获取Field() will clear flagEmbedRO | 指针类型? | 当使用ptr.Elem()或slice.Index()时置位 | 是否为函数类型,即Kind == 19(reflect.Func) | 方法数,函数类型方法数为0 |
flagStickyRO|flagIndir|flagAddr 额外有:
当调用 func (v Value) Field(i int) Value 时:
//返回的Value Inherit permission bits from v, but clear flagEmbedRO.
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
if !field.name.isExported() {
if field.embedded() {
fl |= flagEmbedRO
} else {
fl |= flagStickyRO
}
}
其余参数定义如下:
| - | Kind Width | Method Shift | RO |
|---|---|---|---|
| 值 | 5 | 10 | flagStickyRO | flagEmbedRO |
| 解释 | 种类数量共27个,五位bit可容纳2^5=32个类型 | MethodNum=flag>>10 | 变量只读 |
| 用处 | 表明类型占用bit数 | MethodNum变量偏移量 | ReadOnly只读, 当非RO时才可转换为Interface() |
结论
CanInterface
满足条件: not flag.RO即可读写
相当于满足条件:not flagStickyRO or not flagEmbedRO
flagStickyRO: 初步猜测是普通公有成员
flagEmbedRO: 初步猜测是匿名成员
若有老哥明白,希望帮忙纠正一下
已知:结构体内私有变量 CanInterface == false
CanAddress
满足条件:有指针变量t即TypeOf(t).Kind() == ptr 且 v = ValueOf(t).Elem() 此时v是CanAddress
通常理解是传地址给ValueOf并且使用Elem()取值
CanSet
满足条件: not flag.RO and flag.Addr
相当于满足条件:CanInterface and CanAddress
通常理解是可读写可寻址
关于rtype: sync with ../runtime/type.go:/^type._type.
更底层了,待续