Identify non builtin-types using reflect - go

I need to differentiate such types as
type A []byte
from a []byte. Using reflect, reflect.TypeOf(A{}).Kind tells me that it is a Slice of byte. How can I differentiate []byte{} from A{}, without having a bounded list of types to check for?
Are there new ways to do it in newer versions of Go?

Some background
First let's clear some things related to types. Quoting from Spec: Types:
A type determines the set of values and operations specific to values of that type. Types may be named or unnamed. Named types are specified by a (possibly qualified) type name; unnamed types are specified using a type literal, which composes a new type from existing types.
So there are (predeclared) named types such as string, int etc, and you may also create new named types using type declarations (which involves the type keyword) such as type MyInt int. And there are unnamed types which are the result of a type literal (applied to / including named or unnamed types) such as []int, struct{i int}, *int etc.
You can get the name of a named type using the Type.Name() method, which "returns an empty string for unnamed types":
var i int = 2
fmt.Printf("%q\n", reflect.TypeOf("abc").Name()) // Named: "string"
fmt.Printf("%q\n", reflect.TypeOf(int(2)).Name()) // Named: "int"
fmt.Printf("%q\n", reflect.TypeOf([]int{}).Name()) // Unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(struct{ i int }{}).Name()) // Unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(&struct{ i int }{}).Name()) // Unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(&i).Name()) // Unnamed: ""
There are types which are predeclared and are ready for you to use them (either as-is, or in type literals):
Named instances of the boolean, numeric, and string types are predeclared. Composite types—array, struct, pointer, function, interface, slice, map, and channel types—may be constructed using type literals.
Predeclared types are:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
You may use Type.PkgPath() to get a named type's package path, which "if the type was predeclared (string, error) or unnamed (*T, struct{}, []int), the package path will be the empty string":
fmt.Printf("%q\n", reflect.TypeOf("abc").PkgPath()) // Predeclared: ""
fmt.Printf("%q\n", reflect.TypeOf(A{}).PkgPath()) // Named: "main"
fmt.Printf("%q\n", reflect.TypeOf([]byte{}).PkgPath()) // Unnamed: ""
So you have 2 tools available to you: Type.Name() to tell if the type is a named type, and Type.PkgPath() to tell if the type is not predeclared and is a named type.
But care must be taken. If you use your own, named type in a type literal to construct a new type (e.g. []A), that will be an unnamed type (if you don't use the type keyword to construct a new, named type):
type ASlice []A
fmt.Printf("%q\n", reflect.TypeOf([]A{}).PkgPath()) // Also unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf(ASlice{}).PkgPath()) // Named: "main"
What can you do in such cases? You may use Type.Elem() to get the type's element type, if type's Kind is Array, Chan, Map, Ptr, or Slice (else Type.Elem() panics):
fmt.Printf("%q\n", reflect.TypeOf([]A{}).Elem().Name()) // Element type: "A"
fmt.Printf("%q\n", reflect.TypeOf([]A{}).Elem().PkgPath()) // Which is named, so: "main"
Summary
Type.PkgPath() can be used to "filter out" predeclared and unnamed types. If PkgPath() returns a non-empty string, you can be sure it's a "custom" type. If it returns an empty string, it still may be an unnamed type (in which case Type.Name() returns "") constructed from a "custom" type; for that you may use Type.Elem() to see if it is constructed from a "custom" type, which may have to be applied recursively:
// [][]A -> Elem() -> []A which is still unnamed: ""
fmt.Printf("%q\n", reflect.TypeOf([][]A{}).Elem().PkgPath())
// [][]A -> Elem() -> []A -> Elem() -> A which is named: "main"
fmt.Printf("%q\n", reflect.TypeOf([][]A{}).Elem().Elem().PkgPath())
Try all the examples on the Go Playground.
Special case #1: Anonymous struct types
There is also the case of an anonymous struct type which is unnamed, but it may have a field of a "custom" type. This case can be handled by iterating over the fields of the struct type and performing the same check on each field, and if any of them is found to be a "custom" type, we can claim the whole struct type to be "custom".
Special case #2: Map types
In case of maps we may consider an unnamed map type "custom" if any of its key or value type is "custom".
The value type of a map can be queried with the above mentioned Type.Elem() method, and the key type of a map can be queried with the Type.Key() method - we also have to check this in case of maps.
Example implementation
func isCustom(t reflect.Type) bool {
if t.PkgPath() != "" {
return true
}
if k := t.Kind(); k == reflect.Array || k == reflect.Chan || k == reflect.Map ||
k == reflect.Ptr || k == reflect.Slice {
return isCustom(t.Elem()) || k == reflect.Map && isCustom(t.Key())
} else if k == reflect.Struct {
for i := t.NumField() - 1; i >= 0; i-- {
if isCustom(t.Field(i).Type) {
return true
}
}
}
return false
}
Testing it (try it on the Go Playground):
type K int
var i int = 2
fmt.Println(isCustom(reflect.TypeOf(""))) // false
fmt.Println(isCustom(reflect.TypeOf(int(2)))) // false
fmt.Println(isCustom(reflect.TypeOf([]int{}))) // false
fmt.Println(isCustom(reflect.TypeOf(struct{ i int }{}))) // false
fmt.Println(isCustom(reflect.TypeOf(&i))) // false
fmt.Println(isCustom(reflect.TypeOf(map[string]int{}))) // false
fmt.Println(isCustom(reflect.TypeOf(A{}))) // true
fmt.Println(isCustom(reflect.TypeOf(&A{}))) // true
fmt.Println(isCustom(reflect.TypeOf([]A{}))) // true
fmt.Println(isCustom(reflect.TypeOf([][]A{}))) // true
fmt.Println(isCustom(reflect.TypeOf(struct{ a A }{}))) // true
fmt.Println(isCustom(reflect.TypeOf(map[K]int{}))) // true
fmt.Println(isCustom(reflect.TypeOf(map[string]K{}))) // true

Related

How to get a Protobuf enum value from its string representation?

I can get the string value of a Protobuf enum with this instruction:
str := testPB.Status_ENABLED.String()
How can I perform the inverse operation? (from a string, get the enum element).
The generated code has a map called <EnumName>_value of type map[string]int32. Then you can convert the numerical value to the actual defined type:
num := testPB.Status_value[str]
v := testPB.Status(num)
Consider that if the str value doesn't exist in the map (note that it's case sensitive), the map look-up will return 0. Depending on how you defined your protobuffer, the 0 value might be mapped to a an enum instance that does not have "zero" semantics. This is why it is recommended to map 0 to an "unknown" instance:
enum Status {
UNKNOWN = 0;
ENABLED = 1;
// and so on
}
Which in Go correctly yields a makeshift zero-value if the string representation is effectively unknown:
v := testPB.Status(testPB.Status_value["does_not_exist"])
fmt.Println(v == testPB.Status_UNKNOWN) // true
Go 1.18
With generics, it is possible to write reusable code to construct protobuffer enums from string values:
func Enum[T ~string, PB ~int32](val T, pbmap map[string]int32, dft PB) PB {
v, ok := pbmap[string(val)]
if !ok {
return dft
}
return PB(v)
}
where:
the type parameter T is the string representation, which could also be a type with underlying type string, e.g. type MyEnum string
the argument pbmap is the <EnumName>_value from the generated protobuffer code
the type parameter PB is the protobuffer enum type.
The function above takes a default value of type PB to (obviously) have a fallback in case the string representation is invalid, and also to allow type inference on PB, which otherwise would be used only as a return type.
Usage:
type SomeEnum string
const (
SomeEnumFoo SomeEnum = "FOO"
SomeEnumBar SomeEnum = "BAR"
)
func main() {
foo := SomeEnumFoo
v := Enum(foo, pb.SomeEnum_value, pb.SomeEnum_Foo)
// ^ T ^ map[string]int32 ^ default PB value
// v is type pb.SomeEnum
}

What is the default value of a map of struct

What is the default value of struct in a map? How to check the map value is initialized?
type someStruct struct {
field1 int
field2 string
}
var mapping map[int]someStruct
func main() {
mapping := make(map[int]someStruct)
}
func check(key int) {
if mapping[key] == ? {}
}
Should I check against nil or someStruct{}?
Default value of a struct is zero value for each field which is different on basis of its type.
When storage is allocated for a variable, either through a
declaration or a call of new, or when a new value is created, either
through a composite literal or a call of make, and no explicit
initialization is provided, the variable or value is given a default
value. Each element of such a variable or value is set to the zero
value for its type: false for booleans, 0 for numeric types, "" for
strings, and nil for pointers, functions, interfaces, slices,
channels, and maps. This initialization is done recursively, so for
instance each element of an array of structs will have its fields
zeroed if no value is specified.
type T struct { i int; f float64; next *T }
t := new(T)
the following holds:
t.i == 0
t.f == 0.0
t.next == nil
But for checking the value of a map based on the key if it exists you can use it as:
i, ok := m["route"]
In this statement, the first value (i) is assigned the value stored under the key "route". If that key doesn't exist, i is the value type's zero value (0). The second value (ok) is a bool that is true if the key exists in the map, and false if not.
For your question
Should I check against nil or someStruct{} ?
To check for initialized empty struct you can check for someStruct{} as:
package main
import (
"fmt"
)
type someStruct struct {
field1 int
field2 string
}
var mapping map[int]someStruct
func main() {
var some someStruct
fmt.Println(some == (someStruct{}))
//mapping := make(map[int]someStruct)
}
Go playground

Does type assertion change the value in go?

Go newbie here.
I have a map where the key arguments should be []string.
However, if I try to use the value directly arguments := m["arguments"] it doesn't seem to be the right type. When used later to append to another slice with arguments... I get Cannot use 'arguments' (type interface{}) as type []string.
I fixed this by chaning the assignment to a type check arguments, _ := m["arguments"].([]string). That works, but I'm not sure why. Is type assertion doing conversion as well?
The full example is below:
import (
"github.com/fatih/structs"
"strings"
)
var playbookKeyDict = map[string]string{
"Playbook": "",
"Limit" : "--limit",
"ExtraVars" : "--extra-vars",
}
type Playbook struct {
Playbook string `json:"playbook" xml:"playbook" form:"playbook" query:"playbook"`
Limit string `json:"limit" xml:"limit" form:"limit" query:"limit"`
ExtraVars string `json:"extra-vars" xml:"extra-vars" form:"extra-vars" query:"extra-vars"`
Arguments []string `json:"arguments" xml:"arguments" form:"arguments" query:"arguments"`
Args []string
}
func (p *Playbook) formatArgs() {
// is it worth iterating through directly with reflection instead of using structs import?
// https://stackoverflow.com/questions/21246642/iterate-over-string-fields-in-struct
m := structs.Map(p)
// direct assignment has the wrong type?
// arguments := m["arguments"]
arguments, _ := m["arguments"].([]string)
delete(m, "arguments")
for k, v := range m {
// Ignore non-strings and empty strings
if val, ok := v.(string); ok && val != "" {
key := playbookKeyDict[k]
if key == "" {
p.Args = append(p.Args, val)
} else {
p.Args = append(p.Args, playbookKeyDict[k], val)
}
}
}
p.Args = append(p.Args, arguments...)
}
Type assertion is used to get a value wrapped around using interface.
m := structs.Map(p)
Map(v interface{}){}
Map function is actually taking interface as its argument in the case stated. It is wrapping the type which is []string and its underlying value which is slice. The type can be checked using Relection reflect.TypeOf().
func TypeOf(i interface{}) Type
According to Russ Cox blog on Interfaces
Interface values are represented as a two-word pair giving a pointer
to information about the type stored in the interface and a pointer to
the associated data.
As specified in Golang spec
For an expression x of interface type and a type T, the primary
expression
x.(T)
asserts that x is not nil and that the value stored in x is of type T.
The notation x.(T) is called a type assertion.
For the error part:-
Cannot use 'arguments' (type interface{}) as type []string
We first needs to get the underlying value of type []string from interface using type assertion.

How do I use reflect to check if the type of a struct field is interface{}?

I am using the reflect package to determine the type of a struct field is interface{}
I want to do the comparison like so (where t is a reflect.Type):
if t == reflect.TypeOf(interface{}) {
}
The problem is that the compiler complains: type interface {} is not an expression.
Is there anyway to check if the type of a struct field is an interface{}?
You can get the type of the interface Y by creating a nil instance and using reflection:
yType := reflect.TypeOf((*Y)(nil)).Elem()
and then use the expression
reflect.TypeOf(x).Implements(yType)
to check if the type implements the interface.
Interfaces themselves can not be instantiated. The interface{} interface which is the empty interface is implemented by all types so all fields implement that.
https://play.golang.org/p/gRfheIW_9Y
Actually it also works with the empty interface{} itself but this will always return true (if i'm not missing something):
https://play.golang.org/p/29rWP4LtIo
interface{} is a type, and reflect.TypeOf() expects a value. So you can't pass the literal interface{} to it. You can only pass a value.
Back to the original question. Let's see a struct example:
type My struct {
A int
B interface{}
C io.Reader
}
You want to tell if the type of a field is interface{}. Acquire the reflect.Type of the struct type, then you can access the fields using Type.Field() or Type.FieldByName().
This gives you a value of type reflect.StructField which stores the type of the field.
So far so good. But what should we compare it to? interface{} is an interface type with 0 methods. You can't have (instantiate) a value of that type. You can only have values of concrete types, but yes, they may be wrapped in an interface type.
You could use Type.Kind, and compare it to reflect.Interface, which tells you if it's an interface, but this is true for all interface types. You could also check if it has 0 methods with Type.NumMethod(), which must be 0 for interface{}, but other interfaces could also have 0 methods...
You may use Type.Name, but since interface{} is a unnamed type, its name is the empty string "" (and there are other unnamed types). You may use Type.String() which returns "interface {}" for the empty interface:
t := reflect.TypeOf(My{})
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Printf("Field %q, type: %-12v, type name: %-8q, is interface{}: %v\n",
f.Name, f.Type,
f.Type.Name(),
f.Type.String() == "interface {}",
)
}
Output (try it on the Go Playground):
Field "A", type: int , type name: "int" , is interface{}: false
Field "B", type: interface {}, type name: "" , is interface{}: true
Field "C", type: io.Reader , type name: "Reader", is interface{}: false
You might find this related question interesting / useful: Identify non builtin-types using reflect

How to set default values in Go structs

There are multiple answers/techniques to the below question:
How to set default values to golang structs?
How to initialize structs in golang
I have a couple of answers but further discussion is required.
One possible idea is to write separate constructor function
//Something is the structure we work with
type Something struct {
Text string
DefaultText string
}
// NewSomething create new instance of Something
func NewSomething(text string) Something {
something := Something{}
something.Text = text
something.DefaultText = "default text"
return something
}
Force a method to get the struct (the constructor way).
From this post:
A good design is to make your type unexported, but provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. Also return an interface type and not a concrete type, and the interface should contain everything others want to do with your value. And your concrete type must implement that interface of course.
This can be done by simply making the type itself unexported. You can export the function NewSomething and even the fields Text and DefaultText, but just don't export the struct type something.
Another way to customize it for you own module is by using a Config struct to set default values (Option 5 in the link). Not a good way though.
One problem with option 1 in answer from
Victor Zamanian is that if the type isn't exported then users of your package can't declare it as the type for function parameters etc. One way around this would be to export an interface instead of the struct e.g.
package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
Name string
Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
return candidate{name, 0} // enforce the default value here
}
Which lets us declare function parameter types using the exported Candidate interface.
The only disadvantage I can see from this solution is that all our methods need to be declared in the interface definition, but you could argue that that is good practice anyway.
There is a way of doing this with tags, which
allows for multiple defaults.
Assume you have the following struct, with 2 default
tags default0 and default1.
type A struct {
I int `default0:"3" default1:"42"`
S string `default0:"Some String..." default1:"Some Other String..."`
}
Now it's possible to Set the defaults.
func main() {
ptr := &A{}
Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...
Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}
Here's the complete program in a playground.
If you're interested in a more complex example, say with
slices and maps, then, take a look at creasty/defaultse
From https://golang.org/doc/effective_go.html#composite_literals:
Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from package os.
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
What about making something like this:
// Card is the structure we work with
type Card struct {
Html js.Value
DefaultText string `default:"html"` // this only works with strings
}
// Init is the main function that initiate the structure, and return it
func (c Card) Init() Card {
c.Html = Document.Call("createElement", "div")
return c
}
Then call it as:
c := new(Card).Init()
I found this thread very helpful and educational. The other answers already provide good guidance, but I wanted to summarize my takeaways with an easy to reference (i.e. copy-paste) approach:
package main
import (
"fmt"
)
// Define an interface that is exported by your package.
type Foo interface {
GetValue() string // A function that'll return the value initialized with a default.
SetValue(v string) // A function that can update the default value.
}
// Define a struct type that is not exported by your package.
type foo struct {
value string
}
// A factory method to initialize an instance of `foo`,
// the unexported struct, with a default value.
func NewFoo() Foo {
return &foo{
value: "I am the DEFAULT value.",
}
}
// Implementation of the interface's `GetValue`
// for struct `foo`.
func (f *foo) GetValue() string {
return f.value
}
// Implementation of the interface's `SetValue`
// for struct `foo`.
func (f *foo) SetValue(v string) {
f.value = v
}
func main() {
f := NewFoo()
fmt.Printf("value: `%s`\n", f.GetValue())
f.SetValue("I am the UPDATED value.")
fmt.Printf("value: `%s`\n", f.GetValue())
}
One way to do that is:
// declare a type
type A struct {
Filed1 string
Field2 map[string]interface{}
}
So whenever you need a new variable of your custom defined type just call the NewA function also you can parameterise the function to optionally assign the values to the struct fields
func NewA() *A {
return &A{
Filed1: "",
Field2: make(map[string]interface{}),
}
}
for set default values in Go structs we use anonymous struct:
Person := struct {
name string
age int
city string
}{
name: "Peter",
age: 21,
city: "Noida",
}
fmt.Println(Person)
Structs
An easy way to make this program better is to use a struct. A struct is a type which contains named fields. For example we could represent a Circle like this:
type Circle struct {
x float64
y float64
r float64
}
The type keyword introduces a new type. It's followed by the name of the type (Circle), the keyword struct to indicate that we are defining a struct type and a list of fields inside of curly braces. Each field has a name and a type. Like with functions we can collapse fields that have the same type:
type Circle struct {
x, y, r float64
}
Initialization
We can create an instance of our new Circle type in a variety of ways:
var c Circle
Like with other data types, this will create a local Circle variable that is by default set to zero. For a struct zero means each of the fields is set to their corresponding zero value (0 for ints, 0.0 for floats, "" for strings, nil for pointers, …) We can also use the new function:
c := new(Circle)
This allocates memory for all the fields, sets each of them to their zero value and returns a pointer. (*Circle) More often we want to give each of the fields a value. We can do this in two ways. Like this:
c := Circle{x: 0, y: 0, r: 5}
Or we can leave off the field names if we know the order they were defined:
c := Circle{0, 0, 5}
type Config struct {
AWSRegion string `default:"us-west-2"`
}

Resources