How to get the element type of slice? - go

If there is a struct like:
type A struct {
Arr []int
}
How can I get the element type in the slice arr?
for example, an empty A instance is passed in, how can I get the int type?
func PrintElementType(obj interface{}) {
objType := reflect.TypeOf(obj)
for i := 0; i < objType.NumField(); i++ {
fieldType := objType.Field(i).Type
// here I got 'slice'
fmt.Println(fieldType.Kind())
// here I got '[]int', I think 'int' type is stored somewhere...
fmt.Println(fieldType)
// so, is there any way to get 'int' type?
fmt.Println("inner element type?")
}
}
func main() {
a := A{}
PrintElementType(a)
}

If you have the reflect.Type type descriptor of a slice type, use its Type.Elem() method to get the type descriptor of the slice's element type:
fmt.Println(fieldType.Elem())
Type.Elem() may also be used to get the element type if the type's Kind is Array, Chan, Map, Ptr, or Slice, but it panics otherwise. So you should check the kind before calling it:
if fieldType.Kind() == reflect.Slice {
fmt.Println("Slice's element type:", fieldType.Elem())
}
This will output (try it on the Go Playground):
slice
[]int
Slice's element type: int

Related

cast interface{} to []interface{}

How can I cast an interface{} to []interface{} ?
rt := reflect.ValueOf(raw)
switch rt.Kind() {
case reflect.Slice:
src := raw.([]interface{}) //this operation errors out
for _,_ := range src {
//some operation
}
}
I get an error panic: interface conversion: interface {} is []string, not []interface {}
I want make this method generic enough to handle any type, not a fixed type.
I'm very new to Go and I'm stuck with this problem, most likely I'm doing it wrong. Any suggestion how can I get around this ?
Edit:
Some operation is json.Marshal which return byte array.
What I'm really trying to do:
I have a function that receives interface type, if it is an array/slice then I would like to run json.Marshal on each item rather than apply it as a whole. Basically, I'm trying to break up the JSON blob if the first level object is an array, and yes it needs to be generic.
As the error message states, a []string is not an []interface{}. See the FAQ for an explanation.
Use the reflect API do to this generically:
v := reflect.ValueOf(raw)
switch v.Kind() {
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
elem := v.Index(i).Interface()
// elem is an element of the slice
}
}
Run it on the Playground.

How do I use reflect.DeepEqual() to compare a pointer's value against the zero value of its type?

I need a generic function to check whether something is equal to its zero-value or not.
From this question, I was able to find a function that worked with value types. I modified it to support pointers:
func isZeroOfUnderlyingType(x interface{}) bool {
rawType := reflect.TypeOf(x)
//source is a pointer, convert to its value
if rawType.Kind() == reflect.Ptr {
rawType = rawType.Elem()
}
return reflect.DeepEqual(x, reflect.Zero(rawType).Interface())
}
Unfotunately, this didn't work for me when doing something like this:
type myStruct struct{}
isZeroOfUnderlyingType(myStruct{}) //Returns true (works)
isZeroOfUnderlyingType(&myStruct{}) //Returns false (doesn't) work
This is because &myStruct{} is a pointer and there is no way to dereference an interface{} inside the function. How do I compare the value of that pointer against the zero-value of its type?
reflect.Zero() returns a reflect.Value. reflect.New() returns a pointer to a zero value.
I updated the function to check the case where x is a pointer to something:
func isZeroOfUnderlyingType(x interface{}) bool {
rawType := reflect.TypeOf(x)
if rawType.Kind() == reflect.Ptr {
rawType = rawType.Elem()
return reflect.DeepEqual(x, reflect.New(rawType).Interface())
}
return reflect.DeepEqual(x, reflect.Zero(rawType).Interface())
}

Slicing a pointer to slice

When I'm following this golang blog post about arrays and slices, I tried to pass a pointer to a slice to a function that modify the underlying len property in the slice header:
func PtrSubtractOneFromLength(slicePtr *[]byte) {
slice := *slicePtr
*slicePtr = slice[0 : len(slice)-1]
}
And when I tried to refactor it to this from:
func PtrSubtractOneFromLength(slicePtr *[]int) {
*slicePtr = *slicePtr[0 : len(*slicePtr)-1]
}
I get this error
cannot slice slicePtr (type *[]int)
Where is the magic in the slice := *slicePtr statement?
A slice expression binds stronger than a dereference. Try this:
*slicePtr = (*slicePtr)[0 : len(*slicePtr)-1]

In golang, how to embed on custom type?

I have custom types Int64Array, Channel and ChannelList like:
type Int64Array []int64
func (ia *Int64Array) Scan(src interface{}) error {
rawArray := string(src.([]byte))
if rawArray == "{}" {
*ia = []int64{}
} else {
matches := pgArrayPat.FindStringSubmatch(rawArray)
if len(matches) > 1 {
for _, item := range strings.Split(matches[1], ",") {
i, _ := strconv.ParseInt(item, 10, 64)
*ia = append(*ia, i)
}
}
}
return nil
}
func (ia Int64Array) Value() (driver.Value, error) {
var items []string
for _, item := range ia {
items = append(items, strconv.FormatInt(int64(item), 10))
}
return fmt.Sprintf("{%s}", strings.Join(items, ",")), nil
}
type Channel int64
type ChannelList []Channel
How can I embed Int64Array to ChannelList such that I can call Scan and Value methods on it? I tried the following:
type ChannelList []Channel {
Int64Array
}
but I'm getting syntax error. What's important is to make sure ChannelList items are of type Channel, if this isn't possible via embedding I might just create stand-alone functions to be called by both ChannelList and Int64Array.
An anonymous (or embedded field) is found in a struct (see struct type), not in a type alias (or "type declaration").
You cannot embed a type declaration within another type declaration.
Plus, as illustrated by the answers to "Go: using a pointer to array", you shouldn't be using pointers to slice, use directly the slice themselves (passed by value).
Wessie kindly points out in the comments that (ia *Int64Array) Scan() uses pointer to a slice in order to mutate the underlying array referenced by said slice.
I would prefer returning another slice instead of mutating the existing one.
That being said, the Golang Code Review does mention:
If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.

Create a slice of type from a pointer to a type

Trying to create a slice in which the type is set dynamicaly based on a pointer to a specific type, so i made the following sample
func main() {
var chicken *Chicken
//create a slice of chickens
chickens:=GetaDynamiclyTypedSlice(chicken)
//this throws cannot range over chickens (type *[]interface {}) and i cant figure how to create a slice using my above chicken pointer
for _,chicken := range chickens{
fmt.Println(chicken)
}
}
type Chicken struct{
Weight float64
}
func GetaDynamiclyTypedSlice(ptrItemType interface{})*[]interface {}{
var collection []interface{}
itemtyp := reflect.TypeOf(ptrItemType).Elem()
for i:=0;i<1000;i++{
//create an item of the wanted type
item := reflect.New(itemtyp)
//set a random float to the weight value
item.Elem().FieldByName("Weight").SetFloat(rnd.ExpFloat64())
collection = append(collection,&item)
}
return &collection
}
what should i do to be able to use range on the returned slice?
how can i use the itemtyp as the type of my slice?
There are few problems with your code.
You're returning a pointer to a reflect.Value, 99% sure that's not what you're trying to achive.
You're not dereferencing the slice like Simon mentioned.
Slices are pointer types, if you're returning *[]interface{} for performance reasons, you're actually hurting not helping.
So let's rewrite the code and optimize it! (it's late night SO, time to party):
// pass the size to preallocate the slice, also return the correct slice type.
func GetaDynamiclyTypedSlice(ptrItemType interface{}, size int) (col []interface{}) {
col = make([]interface{}, size)
itemtyp := reflect.TypeOf(ptrItemType).Elem()
for i := range col { //prettier than for i := 0; etc etc
item := reflect.New(itemtyp)
item.Elem().FieldByName("Weight").SetFloat(rand.ExpFloat64())
col[i] = item.Interface() //this is the magic word, return the actual item, not reflect.Value
}
return
}
playground
You just need to dereference the pointer (so you're not iterating over a pointer - you're iterating over a slice):
for _, chicken := range *chickens {
// ...
}
Playground link: http://play.golang.org/p/NBv9sooqEV

Resources