Convert struct to *interface{} - go

I'm using checkr/goflagr, specifically trying to build an EvalContext and fill in the EntityContext field (some content removed below for the sake of brevity)
type EvalContext struct {
EntityContext *interface{} `json:"entityContext,omitempty"`
// flagID
FlagID int64 `json:"flagID,omitempty"`
}
This code is generated by SwaggerGen, and I'm not sure how I can send in a struct to this function without having some awful workaround.
My intuition said I should be able to just pass the address of an instance of a struct, e.g.
type entityContext struct {
Environment string `json:"environment"`
}
entityContextImpl := entityContext{Environment:"prod"}
eval_ctx := goflagr.EvalContext{
FlagID: int64(19),
EntityContext: &entityContextImpl,
}
But this fails with
cannot use &entityContextImpl (type *entityContext) as type *interface {} in field value:
*interface {} is pointer to interface, not interface
I've gotten it to work with the following workaround:
func convertToStarInterface(i interface{}) *interface{} {
return &i
}
func myFunc() {
type entityContext struct {
Environment string `json:"environment"`
}
entityContextImpl := entityContext{Environment:"prod"}
ec := goflagr.EvalContext{
FlagID: int64(18),
EntityContext: convertToStarInterface(entityContextImpl),
}
}
But it feels like there must be a way around having to use this converter function

An *interface{} is as ugly as it can get. It is perfectly possible to implement omitempty with an interface{}, because an interface{} can be nil. You should consider submitting a bug for this. Anyhow...
If you need to pass an *interface{}, you can do it:
var i interface{}
i=someStruct{}
ctx.EntityContext=&i
Your function works as well, however, it is not necessarily efficient or easier on the eye.

Related

Is there a way to store generic structs in a map to reference them in a dynamic way?

I'm looking for way to store some objects (which have a common behaviour but related to different kinds of structs) and access them through a keyword. Something like this:
package main
func main() {
printers := make(map [string] any)
printers["floatPrefix"] = PrefixPrinter[float64]{""}
printers["stringFormat"] = FormatPrinter[string]{""}
}
type IPrinter[T any] interface {
PPrint(value T)
}
type PrefixPrinter[T any] struct {
prefix string
}
func (printer PrefixPrinter[T]) PPrint(value T) {
}
type FormatPrinter[T any] struct {
format string
}
func (printer FormatPrinter[T]) PPrint(value T) {
}
But then, it appears that my printers doesn't implement IPrinter[any]. Off course doing something like printers := make(map [IPrinter[any]] string) allows me to store them, but then I will have to cast any of the Printers, and that doesn't seem to be maintainable. So, would you know a way to implement this behavior, in order to have a single map to store them and that allows me to know that each element stored on it implements IPrinter interface?
Maybe you could make your way changing the interface to the print function to receive "any" and then cast it to the generic type, like:
type IPrinter[T any] interface {
fprint(value any)
}
type PrefixPrinter[T any] struct {
prefix string
}
func (printer PrefixPrinter[T]) fprint(value any) {
tValue := value.(T)
*do stuff*
}
type FormatPrinter[T any] struct {
format string
}
func (printer FormatPrinter[T]) fprint(value any) {
tValue := value.(T)
*do stuff*
}
That way it would be OK to add different generic types:
func main() {
printers := make(map[string]IPrinter[any])
printers["first"] = PrefixPrinter[float64]{""}
printers["second"] = FormatPrinter[string]{""}
}
Three things. One, you are trying to append to a map, which is not Go valid code. Also, you should not use "print" as a variable or function name, as it is a Go keyword.
And third, you can have a map or an slice of an interface type, but having generics it requires all elements to have the same generic type.
For example, taking your code and fixing for using a map of string keys and IPrinter values, with float64 generic type, you could do:
func main() {
printers := make(map[string]IPrinter[float64])
printers["first"] = PrefixPrinter[float64]{""}
printers["second"] = FormatPrinter[float64]{""}
}
Notice that PrefixPrinter and FormatPrinter have float64, and the type is explicit in the map.

Create slice with dynamic struct

I'm trying to create function that receives empty struct and returns slice of that struct type.
For example -
type MyStruct struct {
Id int
}
type MyOtherStruct struct {
Name string
}
getDynamicSlice(MyStruct{}) // will return []MyStruct{}
getDynamicSlice(MyOtherStruct{}) // will return []MyOtherStruct{}
What would be the best way to implement such function in Go?
You could use a type switch:
func getStruct(i interface{}) interface{} {
switch s := i.(type) {
case MyStruct:
// do something here with type MyStruct
s.ID = 3
return s
case MyOtherStruct:
// ...
s.Name = "abc"
return s
}
return i
}
https://play.golang.org/p/iTlYP9yYuQw
BUT you should handle this carefully, because Go is strictly with types. You should respect this.
When you call that function you should use then a type assertion, to get the correct type again:
s, ok := getStruct(MyStruct{}).(MyStruct)
if !ok {
// ...
}
fmt.Println(s)
Some more Information about that pattern
Because a lot of comments under the question are about not using interface{} I want to write something more about the use case here. Interfaces in general have no concrete type information. It does not matter if you are using interface{} or io.Reader. Both interfaces does not allow you to access parameters of the value under the interface. You should always think about this, when you are talking about empty interfaces.
So if your function returns an interface you will allways have this kind of problem. I think almost everybody already made a lot of functions, which are returning an interface. Because error is also just an interface. So the whole error handling can use type-switches like this.

Creating array of struct dynamically in golang

I try to create a generic function that accepts any struct value and create a array of that struct type. Here is the code I tried. But I get the error "t is not a type". How can I implement this.
type RegAppDB struct {
nm string
data []interface{}
}
func CreateRegTable(tbl string, rec interface{}) RegAppDB {
t := reflect.TypeOf(rec)
fmt.Println(t)
return RegAppDB{"log", []t}
}
Go does not support generics, and any attempt to do something like that is not going to work out well. In your specific case, there are a couple of key problems:
You cannot use a variable as a type. Go is compile-time static typed, so anything that gets type information at runtime (i.e. reflect.TypeOf) happens too late to use the way you're trying to do it.
Equally important, your struct's field is of type []interface{}, which means the only type you can use for that field is []interface{}. []string, for example, is a different type, and cannot be assigned to that field.
I took another route. Need some beautification. But this works. So now data is an array of interface and from calling function I pass pointer to structure variables to Save function.
type RegAppDB struct {
nm string
data []interface{}
cnt int
}
// CreateRegTable creates a data structure to hold the regression result
func CreateRegTable(tbl string) *RegAppDB {
return &RegAppDB{tbl, make([]interface{}, 20), 0}
}
// Save implements saving a record Regression application DB
func (rga *RegAppDB) Save(rec interface{}) error {
rga.data[rga.cnt] = rec
rga.cnt++
return nil
}
// Show implements showing the regression table
func (rga *RegAppDB) Show() error {
fmt.Println(rga.cnt)
for i := 0; i <= rga.cnt; i++ {
fmt.Println(rga.data[i])
}
return nil
}
// Compare compares two regression table for equality
func (rga *RegAppDB) Compare(rgt *RegAppDB) bool {
return reflect.DeepEqual(rga, rgt)
}
It cannot be done for a generic type. If there are a fixed number of possible types, then you can do something like the following:
type RegAppDB struct {
nm string
data interface{}
}
func CreateRegTable(rec interface{}) RegAppDB {
switch rec.(type) {
case int:
return &RegAppDB{"log", []int{}}
case string:
return &RegAppDB{"log", []string{}}
}
return nil
}
Note: Your data in RegAppDB should be of type interface{} since []int implements interface{} but not []interface{}

Passing interface{} or []interface{} in Golang

With this snippet, why does it allow interface{} to pass into the function but not []interface. And what's the difference? I know what the error says (have commented it into the function), but I'm not sure what the error means.
https://play.golang.org/p/689R_5dswFX
package main
type smsSendRequest struct {
Recipients string `json:"recipients"`
}
// func action(pass interface{}) {
// //works
// }
func action(pass []interface{}) {
//cannot use data (type *smsSendRequest) as type []interface {} in argument to action
}
func main() {
to := "15551234567"
var data = &smsSendRequest{
Recipients: to,
}
action(data)
}
The type interface{} can be used as a very generic type that would allow any other type to be assigned to it.
So if a function receives interface{}, you can pass any value to it.
That is because in Go, for a type to satisfy an interface it must just implement all methods the interface declares.
Since interface{} is an empty interface, any type will satisfy it.
On the other hand, for a type to satisfy []interface{} it must be an actual slice of empty interfaces.
So if you need a generic function that can receive any value, just use interface{} as you show in your example.
Note that interface{} will allow you to pass in either value or pointer references, so you can pass in pointers or values indistinctly to that function.

How to use passed struct in function

I saw somewhere, but I do not remember where, that a slice struct is passing through function like the following code snippet.
package main
import "fmt"
func passSlice(arg interface{}) {
fmt.Println(arg)
}
func main() {
res := []struct {
Name string
}{}
passSlice(res)
}
I have no idea, how to use here a slice struct in the function. Have someone a idea, how can I use it in the function?
In order to use the slice struct (or any other value stored in an interface), you must first do a type assertion or type switch:
Type assertion:
func passSlice(arg interface{}) {
// Put args value in v if it is of type []struct{ Name string }
v, ok := arg.([]struct{ Name string })
if !ok {
// did not contain a value of type []struct{Name string}
return
}
for _, s := range v {
fmt.Println(s.Name)
}
}
Playground: http://play.golang.org/p/KiFeVC3VQ_
Type switches are similar, but can have cases for multiple types.
There is also an option of using the reflect package, allowing you to more dynamically handle interface values without knowing before hand what types you can expect, but using reflection is also more complex. To know more about using reflection in Golang, you can look here:
Laws of reflection
reflect package

Resources