英文:
How to determine the element type of slice interface{}?
问题
我有以下代码来将切片的长度加倍。
func doubleSlice(s []int) []int  {
  t := make([]int, len(s), (cap(s) + 1) * 2 )
  for i := range s {
    t[i] = s[i]
  }
  return t
}
我想要将这个函数改成可以加倍任何类型的切片。但是我需要先知道元素的类型。
func showInterfaceItem(s interface{}) interface{} {
  if reflect.TypeOf(s).Kind() != reflect.Slice {
    fmt.Println("The interface is not a slice.")
    return
  }
  var t interface{}
  newLen := reflect.ValueOf(s).Len()
  newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
  t = make([]reflect.TypeOf(s), newLen, newCap)
  return t
}
reflect.TypeOf(s) 返回的是 interface{} 的类型,而不是切片元素的类型。我该如何获取切片接口的元素类型?
英文:
I have the following code to double the slice.
func doubleSlice(s []int) []int  {
  t := make([]int, len(s), (cap(s) + 1) * 2 )
  for i := range s {
    t[i] = s[i]
  }
  return t
}
I want to make the func to double any type of slice. And I need to know the element type first.
func showInterfaceItem(s interface{}) interface{} {
  if reflect.TypeOf(s).Kind() != reflect.Slice {
    fmt.Println("The interface is not a slice.")
    return
  }
  var t interface{}
  newLen := reflect.ValueOf(s).Len()
  newCap := (cap(reflect.ValueOf(s).Cap()) + 1) * 2
  t = make([]reflect.TypeOf(s), newLen, newCap)
  return t
}
The reflect.TypeOf(s) return the type of interface{}, not the type of element. How can I get the element type of slice interface?
答案1
得分: 13
你可以使用reflect.TypeOf(s).Elem()来获取切片元素的类型。
package main
import (
	"fmt"
	"reflect"
)
func doubleSlice(s interface{}) interface{} {
	if reflect.TypeOf(s).Kind() != reflect.Slice {
		fmt.Println("The interface is not a slice.")
		return nil
	}
	v := reflect.ValueOf(s)
	newLen := v.Len()
	newCap := (v.Cap() + 1) * 2
	typ := reflect.TypeOf(s).Elem()
	t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
	reflect.Copy(t, v)
	return t.Interface()
}
func main() {
	xs := doubleSlice([]string{"foo", "bar"}).([]string)
	fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
	ys := doubleSlice([]int{3, 1, 4}).([]int)
	fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
}
输出结果将会是:
data = [foo bar] len = 2 cap = 6
data = [3 1 4] len = 3 cap = 8
在 Go Playground 上可以进行测试。
英文:
You can use reflect.TypeOf(s).Elem()
to get the type of element of slice.
package main
import (
	"fmt"
	"reflect"
)
func doubleSlice(s interface{}) interface{} {
	if reflect.TypeOf(s).Kind() != reflect.Slice {
		fmt.Println("The interface is not a slice.")
		return nil
	}
	v := reflect.ValueOf(s)
	newLen := v.Len()
	newCap := (v.Cap() + 1) * 2
	typ := reflect.TypeOf(s).Elem()
	
	t := reflect.MakeSlice(reflect.SliceOf(typ), newLen, newCap)
	reflect.Copy(t, v)
	return t.Interface()
}
func main() {
	xs := doubleSlice([]string{"foo", "bar"}).([]string)
	fmt.Println("data =", xs, "len =", len(xs), "cap =", cap(xs))
	ys := doubleSlice([]int{3, 1, 4}).([]int)
	fmt.Println("data =", ys, "len =", len(ys), "cap =", cap(ys))
}
The output will be:
data = [foo bar] len = 2 cap = 6
data = [3 1 4] len = 3 cap = 8
Check it in: Go Playground
答案2
得分: 0
这在Go语言中是可行的,并且花了我一整天的时间来发现这个模式。
首先,我们想要获取一个切片的指针,以使gorm库满意,其类型为"*[]Obj"。在Go语言中,我们可以创建一个包装函数来实现:
func makeWrapper(cap uint) interface{} {
    arr := make([]Sth, 0, cap)
    return &arr
}
注意,我们不能直接引用创建的值,因为它可能是需要有一个堆栈空间来存储的内部数据。
// 不起作用的示例
func makeWrapper(cap uint) interface{} {
    return &(make([]Sth, 0, cap))
}
正如之前的答案所述,reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() 返回的是interface{[]Sth}。(这里的typ是指reflect.TypeOf(Sth{}),相当于typ == reflect.TypeOf(v))
因此,我们需要创建一个返回类型为*[]Sth的对象,其中包含一个容量为capacity的切片[]Sth。在理解了目标之后,我们可以编写以下代码:
package main
import (
    "reflect"
)
type Sth struct {
    a, b string
}
func main() {
    af := createSlice(Sth{})
    arr := makeWrapper(10).(*[]Sth)
    println(reflect.TypeOf(arr).String())
    // 等效于 makeWrapper,但我们通过反射来实现
    arr = af(10).(*[]Sth)
    println(reflect.TypeOf(arr).String())
}
func makeWrapper(cap uint) interface{} {
    arr := make([]Sth, 0, cap)
    return &arr
}
func createSlice(v interface{}) func(int) interface{} {
    var typ reflect.Type
    if reflect.ValueOf(v).Kind() == reflect.Ptr {
        typ = reflect.ValueOf(v).Elem().Type()
    } else if reflect.ValueOf(v).Kind() == reflect.Struct {
        typ = reflect.TypeOf(v)
    } else {
        panic("only support instance of struct or pointer of that instance")
    }
    return func(capacity int) interface{} {
        // 创建保存切片的外部对象
        outerObj := reflect.New(reflect.SliceOf(typ))
        // 创建切片并保存到返回值中
        outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
        // 获取外部对象的接口
        return outerObj.Interface()
    }
}
英文:
This is doable in golang and takes me whole day to discover the pattern.
Firstly, we want to get a pointer of slice to make gorm happy, which is has type "*[]Obj". To achieve that in golang, we can create a make wrapper like so:
func makeWrapper(cap uint) interface{} {
	arr:= make([]Sth, 0, cap)
	return &arr
}
Notice that, we can't directly reference the maked value, which might be the book keeping data need to have a stack space to store.
//Not working example
func makeWrapper(cap uint) interface{} {
	return &(make([]Sth, 0, cap))
}
And as the answer before, the reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity).Interface() returns interface{[]Sth}. (the typ here is refer to reflect.TypeOf(Sth{}), which equiv to typ == reflect.TypeOf(v))
Thus we need to create a return object of *[]Sth and the value inside is a slice []Sth with capacity. After understanding the objective, we can have this code:
package main
import (
	"reflect"
)
type Sth struct {
	a, b string
}
func main() {
	af:= createSlice(Sth{})
	arr := makeWrapper(10).(*[]Sth)
	println(reflect.TypeOf(arr).String())
    // equiv to makeWrapper, but we do it via reflection
	arr = af(10).(*[]Sth)
	println(reflect.TypeOf(arr).String())
}
func makeWrapper(cap uint) interface{} {
	arr:= make([]Sth, 0, cap)
	return &arr
}
func createSlice(v interface{}) func(int) interface{} {
	var typ reflect.Type
	if reflect.ValueOf(v).Kind() == reflect.Ptr {
		typ = reflect.ValueOf(v).Elem().Type()
	} else if  reflect.ValueOf(v).Kind() == reflect.Struct {
		typ = reflect.TypeOf(v)
	} else {
		panic("only support instance of struct or pointer of that instance")
	}
	return func(capacity int) interface{}{
        // create the outer object saves our slice
		outerObj:=reflect.New(reflect.SliceOf(typ))
        // create the slice and save it to return
		outerObj.Elem().Set(reflect.MakeSlice(reflect.SliceOf(typ), 0, capacity))
        // retrive the interface of outer object
		return outerObj.Interface()
	}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论