golang reflect value kind of slice - go

fmt.Println(v.Kind())
fmt.Println(reflect.TypeOf(v))
How can I find out the type of the reflect value of a slice?
The above results in
v.Kind = slice
typeof = reflect.Value
When i try to Set it will crash if i create the wrong slice
t := reflect.TypeOf([]int{})
s := reflect.MakeSlice(t, 0, 0)
v.Set(s)
For example []int{} instead of []string{} so I need to know the exact slice type of the reflect value before I create one.

To start, we need to ensure that the we're dealing with a slice by testing: reflect.TypeOf(<var>).Kind() == reflect.Slice
Without that check, you risk a runtime panic. So, now that we know we're working with a slice, finding the element type is as simple as: typ := reflect.TypeOf(<var>).Elem()
Since we're likely expecting many different element types, we can use a switch statement to differentiate:
t := reflect.TypeOf(<var>)
if t.Kind() != reflect.Slice {
// handle non-slice vars
}
switch t.Elem().Kind() { // type of the slice element
case reflect.Int:
// Handle int case
case reflect.String:
// Handle string case
...
default:
// custom types or structs must be explicitly typed
// using calls to reflect.TypeOf on the defined type.
}

Related

Converting private, dynamic type from interface{}

I'm trying to test around an SQL query wherein one of the arguments is a gosnowflake.Array (essentially a wrapper to a slice) using the go-sqlmock package. Normally, something like this requires me to create a value converter, which I have included:
func (opt arrayConverterOption[T]) ConvertValue(v any) (driver.Value, error) {
casted, ok := v.(*[]T)
if ok {
Expect(*casted).Should(HaveLen(len(opt.Expected)))
for i, c := range *casted {
Expect(c).Should(Equal(opt.Expected[i]))
}
} else {
fmt.Printf("Type: %T\n", v)
return v, nil
}
return "TEST_RESULT", nil
}
Now, this function is called for every argument submitted to the query. I use it to test the correctness of the values in the slice or pass the argument through if it isn't one. The problem I'm having is that, when I create a arrayConverterOption[string] and give it a gosnowflake.Array(["A", "B", "C"]) as an argument, the type assertion fails because gosnowflake.Array returns an internal dynamic type, *stringArray, which is defined as a *[]string.
So you can see my dilemma here. On the one hand, I can't convert v because it's an interface{} and I can't alias v because the inner type is not *[]string, but *stringArray. So then, what should I do here?
I didn't find a way to do this without resulting to reflection. However, with reflction I did manage it:
var casted []T
var ok bool
value := reflect.ValueOf(v)
if value.Kind() == reflect.Pointer {
if inner := value.Elem(); inner.Kind() == reflect.Slice {
r := inner.Convert(reflect.TypeOf([]T{})).Interface()
casted, ok = r.([]T)
}
}
So, this code checks specifically for anything that is a pointer to a slice, which my dynamic type is. Then it uses reflection to convert the inner object to the slice type I was expecting. After that, I call Interface() on the result to get the interface{} from the reflected value and then cast it to a []T. This succeeds. If it doesn't then I'm not working with one of those dynamically typed slices and I can handle the type normally.

How to cast multiple variables' interfaces to dynamic types together

I know that for a single variable x, to check if it is of a certain type B, just do
switch b.(type) {
case *B:
fmt.Println("find it!")
default:
fmt.Println("can't find it")
}
But now I have a slice of 4 variables, and I'd like to know if their types follow a certain pattern (e.g. of type A,B,C,D).
I know I can do it with a tedious forloop, with many ifs and cases wrapping together, but I wonder if there's a more elegant way to achieve what I want.
You could use reflect against some "truth" slice that you define. This function will take in 2 slices and compare their types, returning an error if the types do not match in the same order.
So arr is your []interface{} slice.
exp is the expected slice, such as
// The values don't matter, only the type for the "truth" slice.
exp := []interface{}{int(0), "", Foo{}, Bar{}}
See https://goplay.tools/snippet/5nja8M00DSt
// SameTypes will compare 2 slices. If the slices have a different length,
// or any element is a different type in the same index, the function will return
// an error.
func SameTypes(arr, exps []interface{}) error {
if len(arr) != len(exps) {
return errors.New("slices must be the same length")
}
for i := range arr {
exp := reflect.TypeOf(exps[i])
found := reflect.TypeOf(arr[i])
if found != exp {
return fmt.Errorf("index '%d' expected type %s, got %s", i, exp, found)
}
}
return nil
}
Keep in mind Foo{} and &Foo{} are different types. If you don't care if it's a pointer, you will have to do additional reflect code. You can do this to get the value of the ptr if the type is a pointer.
x := &Foo{}
t := reflect.TypeOf(x)
// If t is a pointer, we deference that pointer
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
// t is now of type Foo

How to range over slice of a custom type

I'm trying to write in Go custom cache for Google DataStore (more precisely - a wrapper around one of existing cache libraries). At cache initialisation, it should accept any custom type of struct (with appropriately-defined datastore fields), which then would be the basis for all items stored. The idea is that cache can be created/initialised for various types which reflect the structure of a particular DataStore entry (CustomEntry)
Approach 1 - store reflect.Type and use it. Problem encountered - can't iterate over a slice of a custom type
type CustomEntry struct {
Data struct {
name string `datastore:"name,noindex"`
address []string `datastore:"address,noindex"`
} `datastore:"data,noindex"`
}
func (cache *MyCache) CacheData(dataQuery string, dataType reflect.Type) {
slice := reflect.MakeSlice(reflect.SliceOf(dataType), 10, 10)
if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), &slice); err != nil {
//handle error
} else {
for i, dataEntry:= range slice {
// ERROR: Cannot range over 'slice' (type Value)
cache.Set(keys[i].Name, dataEntry)
}
}
//usage: Cache.CacheData("Person", reflect.TypeOf(CustomEntry{})
Approach 2 - accept an array of interfaces as arguments. Problem encountered = []CustomEntry is not []interface{}
func (cache *MyCache) CacheData(dataQuery string, dataType []interface{}) {
if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), &dataType); err != nil {
//handle error
} else {
for i, dataEntry:= range slice {
// this seems to work fine
cache.Set(keys[i].Name, dataEntry)
}
}
//usage:
var dataType []CustomEntry
Cache.CacheData("Person", data)
// ERROR: Cannot use 'data' (type []CustomEntry) as type []interface{}
Any suggestions would be highly appreciated.
I have found a solution and thought it might be worth sharing in case anyone else has a similar problem.
The easiest way is to initiate a slice of structs which the DataStore is expected to receive, and then to pass a pointer to it as an argument (interface{}) into the desired function. DataStore, similarly to a few unmarshaling functions (I have tried with JSON package) will be able to successfully append the data to it.
Trying to dynamically create the slice within the function, given a certain Type, which would be then accepted by a function (such as DataStore client) might be quite difficult (I have not managed to find a way to do it). Similarly, passing a slice of interfaces (to allow for easy iteration) only complicates things.
Secondly, in order to iterate over the data (e.g. to store it in cache), it is necessary to:
(1) retrieve the underlying value of the interface (i.e. the pointer itself) - this can be achieved using reflect.ValueOf(pointerInterface),
(2) dereference the pointer so that we obtain access to the underlying, iterable slice of structs - this can be done by invoking .Elem(),
(3) iterate over the underlying slice using .Index(i) method (range will not accept an interface, even if the underlying type is iterable).
Naturally, adding a number of switch-case statements might be appropriate to ensure that any errors are caught rather than cause a runtime panic.
Hence the following code provides a working solution to the above problem:
In main:
var data []customEntry
c.CacheData("Person",&data)
And the function itself:
func (cache *MyCache) CacheData(dataQuery string, data interface{}) error {
if keys, err := DataStoreClient.GetAll(cache.ctx, datastore.NewQuery(dataQuery), data); err != nil {
return err
} else {
s := reflect.ValueOf(data).Elem()
for i := 0; i < s.Len(); i++ {
cache.Set(keys[i].Name, s.Index(i), 1)
}
}
}

Check if struct field is empty

I would like to iterate over the fields of a struct after unmarshalling a JSON object into it and check for the fields whose value was not set (i.e. are empty).
I can get the value of each field and compare that to the reflect.Zero value for the corresponding type
json.Unmarshal([]byte(str), &res)
s := reflect.ValueOf(&res).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
v := reflect.ValueOf(f.Interface())
if (reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())) {
....
But the problem, of course, is that this will not work well for bool or int values.
If a bool field is set to false in the JSON or an int field is set to 0, they will be equal to the zero value of their type. The aforementioned check will consider the fields to be uninitialized, even though they actually have a value set.
I know one way around this is to use pointers, but I just don't see how that would be possible in this case as I'm working with reflect.Value types, not the actual struct.
As you've mentioned, you could use pointers.
The json package can handle unmarshalling values into pointers for you. You've not included the json payload you are trying to unmarshal, or the struct you are unmarshalling into, so I've made up an example.
// json
{
"foo": true,
"number_of_foos": 14
}
// go struct
type Foo struct {
Present bool `json:"foo"`
Num int `json:"number_of_foos"`
}
Here if the keys foo or number_of_foos is missing, then as you've correctly observed, the zero value (false/ 0) will be used. In general the best advice is to make use of the zero value. Create structures so that zero values of false are useful, rather than a pain.
This is not always possible, so changing the types of the fields in the Foo struct to be pointers will allow you to check the 3 cases you are after.
Present
Present and zero
Missing
here is the same struct with pointers:
// go struct
type Foo struct {
Present *bool `json:"foo"`
Num *int `json:"number_of_foos"`
}
Now you can check for presence of the value with fooStruct.Present != nil and if that condition holds, you can assume that the value in the field is the one you wanted.
There is no need to use the reflect package.
Another way of doing the same is by implementing json.Unmarshaler.
type MaybeInt struct {
Present bool
Value int
}
func (i *MaybeInt) UnmarshalJSON(bs []byte) error {
if e := json.Unmarshal(bs, &i.Value); e != nil {
return e
}
i.Present = true
return nil
}
You can then use MaybeInt in your top-level structure:
type Top struct {
N MaybeInt `json:"n"`
M MaybeInt `json:"m"`
}
func main() {
t := Top{}
if e := json.Unmarshal([]byte(` { "n": 4930 } `), &t); e != nil {
panic(e)
}
fmt.Println(t.N, t.M)
}
See it working on the playground
Try using the golang validator package. The validator package offers a required attribute that might do the required job for your need. The official documentation for required attribute states:
This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.
The example illustrating the same can be seen at: https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go.
Hope this solves your requirement.

get underlying type from a specific interface in golang

As the example, can I get a zero value of its underlying type from the interface Work?
func MakeSomething(w Worker){
w.Work()
//can I get a zeor value type of The type underlying w?
//I tried as followed, but failed
copy :=w
v := reflect.ValueOf(&copy)
fm :=v.Elem()
modified :=reflect.Zero(fm.Type())//fm.type is Worker, and modified comes to be nil
fm.Set(modified)
fmt.Println(copy)
}
type Worker interface {
Work()
}
the playground
Since w contains a pointer to a Worker, you might want to get a zero value of the element it is pointing to. Once you get the element, you can create a zero value of its type:
v := reflect.ValueOf(w).Elem() // Get the element pointed to
zero := reflect.Zero(v.Type()) // Create the zero value
Above code snippet will panic if you pass in a non-pointer to MakeSomething. To prevent this, you might want to do the following instead:
v := reflect.ValueOf(w)
if reflect.TypeOf(w).Kind() == reflect.Ptr {
v = v.Elem()
}
zero := reflect.Zero(v.Type())
If you actually want to have a pointer to a new Worker, you just replace reflect.Zero(v.Type()) with reflect.New(v.Type()).

Resources