How to take address of argument with reflection - go

Basic situation:
you can do this in golang:
func addrOf(foo interface{}) interface{} {
return &foo
}
but you cannot do this:
func addrOf(foo interface{}) interface{} {
fooValue := reflect.ValueOf(foo)
return fooValue.Addr().Interface()
}
Am I doing something wrong? why is foo addressable to the language but not to reflect?

Related

Simplify casting in Go when taking any interface as a parameter

I have a struct like so,
//
// HandlerInfo is used by features in order to register a gateway handler
type HandlerInfo struct {
Fn func(interface{})
FnName string
FnRm func()
}
where I want to pass a func:
func StarboardReactionHandler(e *gateway.MessageReactionAddEvent) {
// foo
}
i := HandlerInfo{Fn: StarboardReactionHandler}
Unfortunately, this results in:
Cannot use 'StarboardReactionHandler' (type func(e *gateway.MessageReactionAddEvent)) as the type func(interface{})
I found this workaround:
func StarboardReactionHandler(e *gateway.MessageReactionAddEvent) {
// foo
}
func handlerCast(e interface{}) {
StarboardReactionHandler(e.(*gateway.MessageReactionAddEvent))
}
i := HandlerInfo{Fn: handlerCast}
Is there some way that I can simplify needing handlerCast, such as doing it inside my StarboardReactionHandler or in HandlerInfo? Maybe with generics or reflection? I basically just want to minimize the syntax / boilerplate that's required here.
you can use interface{}.(type)
follow is a exmple:
package main
import "fmt"
type HandlerInfo struct {
Fn func(interface{})
FnName string
FnRm func()
}
type MessageReactionAddEvent = func(a, b int) int
func StarboardReactionHandler(e interface{}) {
switch e.(type) {
case MessageReactionAddEvent:
fmt.Printf("%v\n", (e.(MessageReactionAddEvent))(1, 2))
}
}
func add(a, b int) int {
return a + b
}
func main() {
i := HandlerInfo{Fn: StarboardReactionHandler}
i.Fn(add)
}

Reflectively set field of struct that was passed in as interface{}

The following works fine:
type MyStruct struct {
MyField int32
}
func SetReflectConcrete(obj *MyStruct, fieldName string, newValue interface{}) {
objElem := reflect.ValueOf(obj).Elem()
field := objElem.FieldByName(fieldName)
field.Set(reflect.ValueOf(newValue))
}
func main() {
myStruct := MyStruct{123}
SetReflectConcrete(myStruct, "MyField", int32{1234})
}
How can I make a variant of the SetReflect function that works on any struct? All my attempts so far have failed. The signature would be something like this:
func SetReflectInterface(obj interface{}, fieldName string, newValue interface{})
And is this even possible, when calling it like
SetReflectInterface(myStruct, "MyField", int32{1234})
or would it have to be called like
SetReflectInterface(&myStruct, "MyField", int32{1234})
(After all, interface{} has a pointer to the struct.)
Declare the argument as type interface{} as you noted. Pass a pointer to the struct as in the last code snippet.
func SetReflectConcrete(obj interface{}, fieldName string, newValue interface{}) {
objElem := reflect.ValueOf(obj).Elem()
field := objElem.FieldByName(fieldName)
field.Set(reflect.ValueOf(newValue))
}
myStruct := MyStruct{123}
SetReflectConcrete(&myStruct, "MyField", int32(1234))
Run it on the playground.
The reflect value must be addressable to set a field. The value will not be addressable if created directly from a struct.

How do I Override UnmarshalJSON Correctly?

I am trying to write a simple custom marshaler and failing. Notice I have an interface that has three functions. Both Happy and Sad structs implement this interface by embedding the emotion struct which implements all the three required functions.
The problem is UnmarshalJSON does not get invoked when I call json.Unmarshal() on the pointer to either Happy or Sad and I can't understand why. You can reproduce the exact codebase in Go Playground or just look below. You will notice that while MarshalJSON is correctly called, UnmarshalJSON isn't.
type Emotion interface {
String() string
MarshalJSON() ([]byte, error)
UnmarshalJSON(data []byte) error
}
type emotion struct {
status string
}
func (s emotion) String() string {
return s.status
}
func (s emotion) MarshalJSON() ([]byte, error) {
fmt.Println("MarshalJSON is overriden: I am called fine")
x := struct {
Status string
}{
Status: s.String(),
}
return json.Marshal(x)
}
func (s *emotion) UnmarshalJSON(data []byte) error {
fmt.Println("MarshalJSON is overriden: I am never called")
y := struct {
Status string
}{
Status: "",
}
err := json.Unmarshal(data, &y)
if err != nil {
return err
}
s.status = y.Status
return nil
}
type Happy struct {
*emotion
}
// Job is not in any detention
type Sad struct {
*emotion
}
func main() {
x := Happy{&emotion{status: "happy"}}
jsonX, _ := json.Marshal(x)
var y Emotion
err := json.Unmarshal(jsonX, &y)
fmt.Printf("%v", err)
}
You cannot unmarshal into an abstract interface type.
An interface value is just a pointer to a type (associating that types methods) - it has no storage behind it - because an abstract type cannot know the exact size of any concrete value it may have in the future.
Using a concrete value type (that also implements that interface) will work:
y2 := emotion{}
err = json.Unmarshal(jsonX, &y2)
Playground: https://play.golang.org/p/8aCEjLgfKVQ
MarshalJSON is overriden: I am called fine
EXPECTED ERROR, Can't unmarshal into a non-concrete value: json: cannot unmarshal object into Go value of type main.Emotion
MarshalJSON is overriden: I am (fixed) and now called
SHOULD NOT ERROR: <nil>
VALUE: happy

assinging functions to function types that return interface values,

Im trying to define a couple of interface to refactor some code and I'm getting a problem where Go is not letting me assign functions to variables.
This is the set up
main.go
type Gettable interface {
Get() int64
}
type MyFunction func(int64) (Gettable, error)
func main() {
var f MyFunction
f = sub.TestFn2
a, _ := f(1)
fmt.Println(a)
}
main/sub
package sub
type MyStruct struct {
Val int64
}
func (v MyStruct) Get() int64 {
return v.Val
}
func TestFn2(a int64) (MyStruct, error) {
return MyStruct{a}, nil
}
I'm trying to define a generic function type, and in the sub package create the concrete functions
and ideally i want to store the functions in a map and call them with something like
FnMap["fnName"]()
I'm not there yet,
im getting an error saying
/topics.go:27:4: cannot use sub.TestFn2 (type func(int64) (sub.MyStruct, error)) as type MyFunction in assignment
but MyStruct clearly implements the interface Gettable
This error occurs because of signature don't match.
You code shared is:
//shared code
//---------------------------------------------
type Gettable interface {
Get() int64
}
type MyFunction func(int64) (Gettable, error)
So you need replace MyStruct by Gettable.
//main/sub
//---------------------------------------------
type MyStruct struct {
Val int64
}
func (v MyStruct) Get() int64 {
return v.Val
}
//this signature of TestFn2 is different of MyStruct
//-[TestFn2] func (a int64) (MyStruct, error)
//-[MyFunction] func(int64) (Gettable, error)
func TestFn2(a int64) (Gettable, error) {//<-- replace by Gettable here
return MyStruct{a}, nil
}
Running your code:
//main.go
//---------------------------------------------
func main() {
var f MyFunction
f = TestFn2
a, _ := f(1)
fmt.Println(a)
}
Result is:
{1}
See in playground: https://play.golang.org/p/sRsXix8E_83
As per the Go's assignability rules a function f is assignable to a variable v only if the variable's type T exactly matches the f's signature.
The ability to assign a more specific type in some other languages called "covariance", and Go's type system does not have it.

Dynamic function call in Go

I'm trying to dynamically call functions returning different types of struct.
For example, let's take the following code.
struct A {
Name string
Value int
}
struct B {
Name1 string
Name2 string
Value float
}
func doA() (A) {
// some code returning A
}
func doB() (B) {
// some code returning B
}
I would like to pass either the function doA or doB as an argument to a generic function that would execute the function and JSON-encode the result. Like the following:
func Generic(w io.Writer, fn func() (interface {}) {
result := fn()
json.NewEncoder(w).Encode(result)
}
But when I do:
Generic(w, doA)
I get the following error:
cannot use doA (type func() (A)) as type func() (interface {})
Is there a way to achieve this dynamic call?
First, let me remark that func() (interface{}) means the same thing as func() interface{}, so I'll use the shorter form.
Passing a function of type func() interface{}
You can write a generic function that takes a func() interface{} argument as long as the function that you pass to it has type func() interface{}, like this:
type A struct {
Name string
Value int
}
type B struct {
Name1 string
Name2 string
Value float64
}
func doA() interface{} {
return &A{"Cats", 10}
}
func doB() interface{} {
return &B{"Cats", "Dogs", 10.0}
}
func Generic(w io.Writer, fn func() interface{}) {
result := fn()
json.NewEncoder(w).Encode(result)
}
You can try out this code in a live playground:
http://play.golang.org/p/JJeww9zNhE
Passing a function as an argument of type interface{}
If you want to write functions doA and doB that return concretely typed values, you can pass the chosen function as an argument of type interface{}. Then you can use the reflect package to make a func() interface{} at run-time:
func Generic(w io.Writer, f interface{}) {
fnValue := reflect.ValueOf(f) // Make a concrete value.
arguments := []reflect.Value{} // Make an empty argument list.
fnResults := fnValue.Call(arguments) // Assume we have a function. Call it.
result := fnResults[0].Interface() // Get the first result as interface{}.
json.NewEncoder(w).Encode(result) // JSON-encode the result.
}
More concisely:
func Generic(w io.Writer, fn interface{}) {
result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
json.NewEncoder(w).Encode(result)
}
Complete program:
package main
import (
"encoding/json"
"io"
"os"
"reflect"
)
type A struct {
Name string
Value int
}
type B struct {
Name1 string
Name2 string
Value float64
}
func doA() *A {
return &A{"Cats", 10}
}
func doB() *B {
return &B{"Cats", "Dogs", 10.0}
}
func Generic(w io.Writer, fn interface{}) {
result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
json.NewEncoder(w).Encode(result)
}
func main() {
Generic(os.Stdout, doA)
Generic(os.Stdout, doB)
}
Live playground:
http://play.golang.org/p/9M5Gr2HDRN
Your return signature is different for these functions:
fn func() (interface {}) vs. func doA() (A) and func doB() (B)
You are getting a compiler error because you are passing a function with a different signature into your Generic function. To address this issue you can change your functions to return interface{}.
This is an example of how to do that, I am using anonymous structs and printing the return value out rather than serializing them but this applies just the same to your example:
package main
import "fmt"
func doA() interface{} {
return struct {
Name string
Value int
}{
"something",
5,
}
}
func doB() interface{} {
return struct {
Name1 string
Name2 string
Value float64
}{
"something",
"or other",
5.3,
}
}
func main() {
fmt.Println("Hello, playground", doA(), doB())
}
Experiment with this in the Go Playground: http://play.golang.org/p/orrJw2XMW8

Resources