preliminary
empty interface,type assertion,type switch
探究这个问题的缘由:使用sort.Slice(slice interface{}, less func(i int, j int) bool)
函数,比如下面的代码示例,我传入sort.Slice
的第一个参数是对象而不是指针,如果算作值传递的话应该会引起对象拷贝,sort.Slice
函数只是在对拷贝做排序,不影响原对象,而实际结果是如图所示即在原对象上进行了排序。
slice := []int{4, 3, 1, 2, 5}
sort.Slice(slice, func(i, j int) bool {
if slice[i] < slice[j] {
return true
}
return false
})
fmt.Println(slice) // [1 2 3 4 5]
这个问题很明显地就出在了interface{}
这个可以代表任何类型的符号上了(或者说任何类型都实现了interface{}
)。在Go Data Structures: Interfaces中解释了interface的数据结构,即一个指向interface table的指针和一个指向被转换为interface的数据的指针,由于在本例中使用的是interface{}
,所以第一个指针可以被简化为指向类型的指针(对应为src/reflect/type.go:296的rtype
),而文中说第二个指针在数据可以fit进一个word的时会直接拷贝,这个我实测倒没有发生。总而言之,在变量a被转换为empty interface变量b之后,b中保存着指向a的数据的指针,而非拷贝。 于是sort.Slice
函数可以利用指针直接改变原有数组的排列。
再来看一下sort.Slice
的实现
func Slice(slice interface{}, less func(i, j int) bool) {
rv := reflect.ValueOf(slice)
swap := reflect.Swapper(slice)
length := rv.Len()
quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
}
reflect.Swapper
函数中其实也使用了reflect.ValueOf
函数,reflect.ValueOf
进而调用了reflect.unpackEface
函数,其实现如下
// unpackEface converts the empty interface i to a Value.
func unpackEface(i interface{}) Value {
e := (*emptyInterface)(unsafe.Pointer(&i))
// NOTE: don't read e.word until we know whether it is really a pointer or not.
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *rtype
word unsafe.Pointer
}
可以看到第一行就利用了上文中描述的interface{}
的数据结构,得到了指向被转换变量的数据的指针,并构造为reflect.Value
对象返回。在此要提到的是,fmt.Printf
对reflect.Value
有着特殊处理(stackoverflow,在src/fmt/print.go中的printArg
和printValue
函数也能看到),所以直接打印的话,虽然reflect.Value
本身为一个struct,但却只打印出其包含的值。
在这里贴一下reflect.Swapper
的代码充个数,其中最主要的也就是对reflect.Value.ptr
的转换和函数构造。
// Swapper returns a function that swaps the elements in the provided
// slice.
//
// Swapper panics if the provided interface is not a slice.
func Swapper(slice interface{}) func(i, j int) {
v := ValueOf(slice)
if v.Kind() != Slice {
panic(&ValueError{Method: "Swapper", Kind: v.Kind()})
}
// Fast path for slices of size 0 and 1. Nothing to swap.
switch v.Len() {
case 0:
return func(i, j int) { panic("reflect: slice index out of range") }
case 1:
return func(i, j int) {
if i != 0 || j != 0 {
panic("reflect: slice index out of range")
}
}
}
typ := v.Type().Elem().(*rtype)
size := typ.Size()
hasPtr := typ.kind&kindNoPointers == 0
// Some common & small cases, without using memmove:
if hasPtr {
if size == ptrSize {
ps := *(*[]unsafe.Pointer)(v.ptr)
return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
}
if typ.Kind() == String {
ss := *(*[]string)(v.ptr)
return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
}
} else {
switch size {
case 8:
is := *(*[]int64)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 4:
is := *(*[]int32)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 2:
is := *(*[]int16)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
case 1:
is := *(*[]int8)(v.ptr)
return func(i, j int) { is[i], is[j] = is[j], is[i] }
}
}
s := (*sliceHeader)(v.ptr)
tmp := unsafe_New(typ) // swap scratch space
return func(i, j int) {
if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) {
panic("reflect: slice index out of range")
}
val1 := arrayAt(s.Data, i, size, "i < s.Len")
val2 := arrayAt(s.Data, j, size, "j < s.Len")
typedmemmove(typ, tmp, val1)
typedmemmove(typ, val1, val2)
typedmemmove(typ, val2, tmp)
}
}