Golang reflect和interface{}

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.Printfreflect.Value有着特殊处理(stackoverflow,在src/fmt/print.go中的printArgprintValue函数也能看到),所以直接打印的话,虽然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)
	}
}

你可能感兴趣的:(golang)