How do I type-assert that a value is a pointer (to a string)? - go

I'm trying to create a method that will return the length of a generic type. If we have a string, we call len(string), or if its an array of interface{} type, we call len() on that as well. This works well, however, it doesnt work in you pass in a pointer to a string (I'm assuming I'd have the same problem with arrays and slices as well). So how can I check if I have a pointer, and dereference it?
func (s *Set) Len(i interface{}) int {
if str, ok := i.(string); ok {
return len(str)
}
if array, ok := i.([]interface{}); ok {
return len(array)
}
if m, ok := i.(map[interface{}]interface{}); ok {
return len(m)
}
return 0
}

You can do the same thing as for the other types:
if str, ok := i.(*string); ok {
return len(*str)
}
At this point you may want to use a type switch instead of the more verbose ifs:
switch x := i.(type) {
case string:
return len(x)
case *string:
return len(*x)
…
}

Related

Return default value for generic type

How do you return nil for a generic type T?
func (list *mylist[T]) pop() T {
if list.first != nil {
data := list.first.data
list.first = list.first.next
return data
}
return nil
}
func (list *mylist[T]) getfirst() T {
if list.first != nil {
return list.first.data
}
return nil
}
I get the following compilation error:
cannot use nil as T value in return statement
You can't return nil for any type. If int is used as the type argument for T for example, returning nil makes no sense. nil is also not a valid value for structs.
What you may do–and what makes sense–is return the zero value for the type argument used for T. For example the zero value is nil for pointers, slices, it's the empty string for string and 0 for integer and floating point numbers.
How to return the zero value? Simply declare a variable of type T, and return it:
func getZero[T any]() T {
var result T
return result
}
Testing it:
i := getZero[int]()
fmt.Printf("%T %v\n", i, i)
s := getZero[string]()
fmt.Printf("%T %q\n", s, s)
p := getZero[image.Point]()
fmt.Printf("%T %v\n", p, p)
f := getZero[*float64]()
fmt.Printf("%T %v\n", f, f)
Which outputs (try it on the Go Playground):
int 0
string ""
image.Point (0,0)
*float64 <nil>
The *new(T) idiom
This has been suggested as the preferred option in golang-nuts. It is probably less readable but easier to find and replace if/when some zero-value builtin gets added to the language.
It also allows one-line assignments.
The new built-in allocates storage for a variable of any type and returns a pointer to it, so dereferencing *new(T) effectively yields the zero value for T. You can use a type parameter as the argument:
func Zero[T any]() T {
return *new(T)
}
In case T is comparable, this comes in handy to check if some variable is a zero value:
func IsZero[T comparable](v T) bool {
return v == *new(T)
}
var of type T
Straightforward and easier to read, though it always requires one line more:
func Zero[T any]() T {
var zero T
return zero
}
Named return types
If you don't want to explicitly declare a variable you can use named returns. Not everyone is fond of this syntax, though this might come in handy when your function body is more complex than this contrived example, or if you need to manipulate the value in a defer statement:
func Zero[T any]() (ret T) {
return
}
func main() {
fmt.Println(Zero[int]()) // 0
fmt.Println(Zero[map[string]int]()) // map[]
fmt.Println(Zero[chan chan uint64]()) // <nil>
}
It's not a chance that the syntax for named returns closely resembles that of var declarations.
Using your example:
func (list *mylist[T]) pop() (data T) {
if list.first != nil {
data = list.first.data
list.first = list.first.next
}
return
}
Return nil for non-nillable types
If you actually want to do this, as stated in your question, you can return *T explicitly.
This can be done when the type param T is constrained to something that excludes pointer types. In that case, you can declare the return type as *T and now you can return nil, which is the zero value of pointer types.
// constraint includes only non-pointer types
func getNilFor[T constraints.Integer]() *T {
return nil
}
func main() {
fmt.Println(reflect.TypeOf(getNilFor[int]())) // *int
fmt.Println(reflect.TypeOf(getNilFor[uint64]())) // *uint64
}
Let me state this again: this works best when T is NOT constrained to anything that admits pointer types, otherwise what you get is a pointer-to-pointer type:
// pay attention to this
func zero[T any]() *T {
return nil
}
func main() {
fmt.Println(reflect.TypeOf(zero[int]())) // *int, good
fmt.Println(reflect.TypeOf(zero[*int]())) // **int, maybe not what you want...
}
You can init a empty variable.
if l == 0 {
var empty T
return empty, errors.New("empty Stack")
}

Print the key/value types of a Golang map

I am trying to print the type of a map, eg: map[int]string
func handleMap(m reflect.Value) string {
keys := m.MapKeys()
n := len(keys)
keyType := reflect.ValueOf(keys).Type().Elem().String()
valType := m.Type().Elem().String()
return fmt.Sprintf("map[%s]%s>", keyType, valType)
}
so if I do:
log.Println(handleMap(make(map[int]string)))
I want to get "map[int]string"
but I can't figure out the right calls to make.
func handleMap(m interface{}) string {
return fmt.Sprintf("%T", m)
}
Try not to use reflect. But if you must use reflect:
A reflect.Value value has a Type() function, which returns a reflect.Type value.
If that type's Kind() is reflect.Map, that reflect.Value is a value of type map[T1]T2 for some types T1 and T2, where T1 is the key type and T2 is the element type.
Therefore, when using reflect, we can pull apart the pieces like this:
func show(m reflect.Value) {
t := m.Type()
if t.Kind() != reflect.Map {
panic("not a map")
}
kt := t.Key()
et := t.Elem()
fmt.Printf("m = map from %s to %s\n", kt, et)
}
See a more complete example on the Go Playground. (Note that both maps are actually nil, so there are no keys and values to enumerate.)

How to create type conditionals for functions that accept interface{} in go?

I'm writing a function that is meant to accept either strings or slices in go. However, when I type my parameters as interface{} I can't perform actions upon those variables even when inside a conditional which checks the type.
Can the compiler deduce that my local variable must be of type Slice once inside my if block? How can I accomplish a for loop over the Slice after I know for certain it is a Slice?
func createFields(keys interface{}, values interface{}) ([]map[string]interface{}, error) {
fields := make([]map[string]interface{}, 1, 1)
if reflect.TypeOf(keys).Kind() == reflect.Slice && reflect.TypeOf(values).Kind() == reflect.Slice {
if len(keys.([]interface{})) != len(values.([]interface{})) {
return fields, errors.New("The number of keys and values must match")
}
// How can I loop over this slice inside the if block?
for i, key := range keys.([]interface{}) {
item := map[string]string{
"fieldID": keys[i], // ERROR: invalid operation: keys[i] (type interface {} does not support indexing)
"fieldValue": values[i],
}
fields.append(item)// ERROR: fields.append undefined (type []map[string]interface {} has no field or method append)
}
return fields, _
}
if reflect.TypeOf(keys).Kind() == reflect.String && reflect.Typeof(values).Kind() == reflect.String {
item := map[string]string{
"fieldID": keys,
"fieldValue": values,
}
fields.append(item)
return fields, _
}
return fields, errors.New("Parameter types did not match")
}
Use type assertions like
keySlice := keys.([]interface{})
valSlice := values.([]interface{})
and work with those from that point onwards. You can even eliminate the use of reflect, like:
keySlice, keysIsSlice := keys.([]interface{})
valSlice, valuesIsSlice := values.([]interface{})
if (keysIsSlice && valuesIsSlice) {
// work with keySlice, valSlice
return
}
keyString, keysIsString := keys.(string)
valString, valuesIsString := values.(string)
if (keysIsString && valuesIsString) {
// work with keyString, valString
return
}
return errors.New("types don't match")
Or you can structure the whole thing as type switches:
switch k := keys.(type) {
case []interface{}:
switch v := values.(type) {
case []interface{}:
// work with k and v as slices
default:
// mismatch error
}
case string:
switch v := values.(type) {
case string:
// work with k and v as strings
default:
// mismatch error
}
default:
// unknown types error
}

Get type of an uninitialized slice

I want to return a type of an interface{}, while the input value might be var m []*MyModel
I've managed to get to the type *MyModel, while MyModel not as a pointer seems to be unreachable to me.
func getType( m interface{} ) reflect.Type {
t := reflect.TypeOf( m );
v := reflect.ValueOf( m );
if t.Kind() == reflect.Ptr {
if v.IsValid() && !v.IsNil() {
return getType( v.Elem().Interface() );
}
panic( "We have a problem" );
}
if t.Kind() == reflect.Slice {
if v.Len() == 0 {
s := reflect.MakeSlice( t , 1 , 1 );
return getType( s.Interface() );
}
return getType( v.Index( 0 ).Interface() );
}
return t;
}
Is it possible?
You may use Type.Elem() to get the type's element type, which works for Array, Chan, Map, Ptr and Slice.
You may run a loop and "navigate" to the type's element type until the type is not a pointer nor a slice (nor array, chan, map if you need so).
So the simple solution is this:
func getElemType(a interface{}) reflect.Type {
for t := reflect.TypeOf(a); ; {
switch t.Kind() {
case reflect.Ptr, reflect.Slice:
t = t.Elem()
default:
return t
}
}
}
Testing it:
type MyModel struct{}
fmt.Println(getElemType(MyModel{}))
fmt.Println(getElemType(&MyModel{}))
fmt.Println(getElemType([]MyModel{}))
fmt.Println(getElemType([]*MyModel{}))
fmt.Println(getElemType(&[]*MyModel{}))
fmt.Println(getElemType(&[]****MyModel{}))
fmt.Println(getElemType(&[][]**[]*[]***MyModel{}))
var p *[][]**[]*[]***MyModel
fmt.Println(p) // It's nil!
fmt.Println(getElemType(p))
Output (try it on the Go Playground):
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
main.MyModel
<nil>
main.MyModel
As you can see, no matter how "deep" we go with slices and pointers (&[][]**[]*[]***MyModel{}), getElemType() is able to extract main.MyModel.
One thing to note is that in my solution I used reflect.Type and not reflect.Value. Go is a statically typed language, so the type information is there even if pointers and slice elements are not "populated", even if we pass a "typed" nil such as p, we're still able to navigate through the "type chain".
Note: The above getElemType() panics if called with an untyped nil value, e.g. getElemType(nil), because in this case there is no type information available. To defend this, you may add a simple check:
if a == nil {
return nil
}
Note #2: Since the implementation contains a loop without limiting iteration count, values of recursive types will drive it into an endless loop, such as:
type RecType []RecType
getElemType(RecType{}) // Endless loop!

Other ways of verifying reflect.Type for int and float64

In golang, a number in JSON message is always parsed into float64.
In order to detect if it is actually integer, I am using reflect.TypeOf() to check its type.
Unfortunately there is no constant that represents reflect.Type.
intType := reflect.TypeOf(0)
floatType := reflect.TypeOf(0.0)
myType := reflect.TypeOf(myVar)
if myType == intType {
// do something
}
Is there more elegant solution instead of using 0 or 0.0 to get reflect.Type?
You may also use the Value.Kind() or Type.Kind() method whose possible values are listed as constants in the reflect package, at the doc of the Kind type.
myType := reflect.TypeOf(myVar)
if k := myType.Kind(); k == reflect.Int {
fmt.Println("It's of type int")
} else if k == reflect.Float64 {
fmt.Println("It's of type float64")
}
You can also use it in a switch:
switch myType.Kind() {
case reflect.Int:
fmt.Println("int")
case reflect.Float64:
fmt.Println("float64")
default:
fmt.Println("Some other type")
}
Note that both reflect.Type and reflect.Value has a Kind() method, so you can use it if you start with reflect.ValueOf(myVar) and also if you start with reflect.TypeOf(myVar).
To check if interface is of a specific type you can use type assertion with two return values, the second return value is a boolean indicating if the variable is of the type specified. And unlike with a single return value, it will not panic if the variable is of a wrong type.
if v, ok := myVar.(int); ok {
// type assertion succeeded and v is myVar asserted to type int
} else {
// type assertion failed, myVar wasn't an int
}
If there's more types that you need to check then using a type switch is a good idea:
switch v := myVar.(type) {
case int:
// v has type int
case float64:
// v has type float64
default:
// myVar was something other than int or float64
}
Note however that neither of these actually solve your problem, because like you say, numbers in JSON documents are always parsed into float64s. So if myVar is a parsed JSON number, it will always have type of float64 instead of int.
To solve this, I suggest you use the UseNumber() method of the json.Decoder, which causes the decoder to parse numbers as type Number, instead of float64. Take a look at https://golang.org/pkg/encoding/json/#Number
// Assume myVar is a value decoded with json.Decoder with UseNumber() called
if n, ok := myVar.(json.Number); ok {
// myVar was a number, let's see if its float64 or int64
// Check for int64 first because floats can be parsed as ints but not the other way around
if v, err := n.Int64(); err != nil {
// The number was an integer, v has type of int64
}
if v, err := n.Float64(); err != nil {
// The number was a float, v has type of float64
}
} else {
// myVar wasn't a number at all
}

Resources