can I define a function on a map[string]interface{} - go

I have tried
func (m map[string]interface{}) Foo() {
...
}
and
func (m *map[string]interface{}) Foo() {
...
}
but go test errors with
invalid receiver type map[string]interface {} (map[string]interface {} is an unnamed type)
so I have to add some more text to keep SO happy here

You need to define a new type to be able to attach a method to it.
package main
import "fmt"
type MyMap map[string]interface{}
func (m MyMap) Foo() {
fmt.Println("You fool!")
}
func main(){
m := new(MyMap)
m.Foo()
}

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)
}

Golang inheritance - Cannot use (type func(d Dog)) as the type func(animal Animal)

I was tring to write a method callGetName which can take both getCatName and getDogName method as its parameter while my IDE tells me:
Cannot use 'getDogName' (type func(d Dog)) as the type func(animal Animal)
package main
type Animal struct {
Name string
}
type Cat struct {
Animal
}
type Dog struct {
Animal
}
func getCatById(c Cat) {}
func validateDogNames(d Dog) {}
func invokeFunc(f func(animal Animal)) {}
func main() {
invokeFunc(getCatById)
invokeFunc(validateDogNames)
}
I tried to analyze the reason, maybe it's because golang supports multiple inheritance?
Please let me know if I was doing something stupid or is there any better way to achieve this?
========
A little more about why I'm trying this: in go-kit framework, I have to write makeEndpoint functions for every single service method defined. And I used reflect to adopt a generic makeEndpoints like below:
func NewProductEndpoints() ProductEndpoints {
ps := service.NewProductService()
return ProductEndpoints{
GetProductById: makeEndpoint(ps, util.GetFunctionName(ps.GetProductById)),
CreateProduct: makeEndpoint(ps, util.GetFunctionName(ps.CreateProduct)),
}
}
func makeEndpoint(s service.ProductService, funcName string) kitEndpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(domain.ProductDTO)
currFunc := reflect.ValueOf(s).MethodByName(funcName)
args := []reflect.Value{reflect.ValueOf(req)}
res := currFunc.Call(args)[0]
return res, nil
}
}
wondering if there's a better way to achieve. Thanks in advance.
So you're thinking in a fairly OOP fashion, Go does not have inheritance (to clarify it has struct embedding which is what you are doing in your first example). We tend to favour composition to solve problems.
One way you could look at solving your problem is like the below.
package main
import (
"fmt"
)
type Namer interface {
Name() string
}
type Cat struct {
name string
}
func (c Cat) Name() string {
return c.name
}
type Dog struct {
name string
}
func (d Dog) Name() string {
return d.name
}
func PetName(n Namer) {
fmt.Println(n.Name())
}
func main() {
PetName(Dog{name: "Fido"})
PetName(Cat{name: "Mittens"})
}
Names can be improved, but it should act as a basic example of an approach that could be taken.
Edit: Example based on comment left below
package main
import (
"fmt"
)
type Invoker interface {
Invoke()
}
type Dog struct{}
func (Dog) Bark() {
fmt.Println("Woof")
}
func (d Dog) Invoke() {
d.Bark()
}
type Cat struct{}
func (Cat) Meow() {
fmt.Println("Meow")
}
func (c Cat) Invoke() {
c.Meow()
}
func CallFunc(i Invoker) {
i.Invoke()
}
func main() {
CallFunc(Cat{})
CallFunc(Dog{})
}

Call Method Expression with Pointer to Struct as Receiver

There is func someFunc(v interface{}, fn interface{}) where v is a pointer to struct (say *A) and fn is a method expression (say (*A).method()). How to call fn with v as parameter (using reflect)?
It's possible to do with reflect.Value.Call(), however it's pretty cumbersome, especially if you need to do something with the return value. But here's a basic example:
package main
import (
"fmt"
"reflect"
)
type Foo struct {
Bar string
}
func concrete(foo *Foo) {
fmt.Printf("Foo: %#v\n", foo)
}
func someFunc(v interface{}, fn interface{}) {
f := reflect.ValueOf(fn)
arg := reflect.ValueOf(v)
f.Call([]reflect.Value{arg})
}
func main() {
foo := Foo{"Bar"}
someFunc(&foo, concrete)
}
// Output: Foo: &main.Foo{Bar:"Bar"}
https://play.golang.org/p/ED6QdvENxti
If you want to call a method of a struct by name, we need to revise it just a bit:
type Foo struct {
Bar string
}
func (f *Foo) Concrete() {
fmt.Printf("Foo: %#v\n", f)
}
func callByName(v interface{}, fn string) {
arg := reflect.ValueOf(v)
f := arg.MethodByName(fn)
f.Call([]reflect.Value{})
}
func main() {
foo := Foo{"Bar"}
callByName(&foo, "Concrete")
}
Notice that in this case the method value is already bound to the struct instance, so we don't need to pass it anything as an argument.
So to the challenge of using reflect to call a method without knowing name and being a method of the same type, as op (orignally) addressed, I have come up with a very, very ugly and unsafe way.
package main
import (
"fmt"
"reflect"
"runtime"
"strings"
)
func getFName(v reflect.Value) string {
return runtime.FuncForPC(v.Pointer()).Name()
}
func callMethod(a,fn interface{}) {
fname:=getFName(reflect.ValueOf(fn))
parts:=strings.Split(fname,".")
name:=strings.TrimSuffix(parts[len(parts)-1],"-fm")
reflect.ValueOf(a).MethodByName(name).Call(nil)
}
func (s *a) Tst() {
fmt.Println(s)
}
type a struct { p string }
func main() {
x,y:=a{"Good"},a{"Bad"}
callMethod(&x,y.Tst)
}
Playground: https://play.golang.org/p/QgrGhI5DC3p
I would like to know a better way that does not rely on how runtime.FuncForPc formats methods (which is not version-safe), but this is the best I have. Please do tell me any ideas to improve it.

Why does method signature have to perfectly match interface method

I began to learn Go and have trouble understanding the following:
package main
import "fmt"
type I interface {
foo(interface {})
}
type S struct {}
func (s S) foo(i int) {
fmt.Println(i)
}
func main() {
var i I = S{}
i.foo(2)
}
This fails with:
cannot use S literal (type S) as type I in assignment:
S does not implement I (wrong type for foo method)
have foo(int)
want foo(interface {})
I don't understand why Go doesn't accept the foo(int) signature given the fact that int implements interface {}. Can anyone help with an explanation?
I think your understanding of interface isn't sound. Interface{} itself is a type. It consists of two things : underlying type and underlying value.
Golang doesn't have overloading. Golang type system matches by name and requires consistency in the types
So, when you are defining a function taking a interface type as a parameter:
foo(interface {})
This is a different function from a function taking int type:
foo(int)
So you should change the following line to
func (s S) foo(i interface{}) {
fmt.Println(i)
}
Or better yet to this:
type I interface {
foo()
}
type S struct {
I int
}
func (s S) foo() {
fmt.Println(s.I)
}
func main() {
var i I = S{2}
i.foo()
}
The error message itself is self-explaining:
"S does not implement I (wrong type for foo method)"
so S{} which is of S type cannot be used on the RHS of type I variable assignment.
To implement I interface, type S needs to define foo function with the exact same signature.
To achieve what you wanted, you can use empty interface as input parameter for your foo function in S and do type assertion
package main
import "fmt"
type I interface {
foo(interface {})
}
type S struct {}
func (s S) foo(i interface{}) {
if i, ok := i.(int); ok{
fmt.Println(i)
}
}
func main() {
var i I = S{}
i.foo(2)
i.foo("2")
}
Try it in go playground

Adding a pointer to a struct to a slice

I'm trying to add a pointer to a struct to a slice, but I can't get rid of this error:
cannot use NewDog() (type *Dog) as type *Animal in append:
*Animal is pointer to interface, not interface
How can I avoid this error? (while still using pointers)
package main
import "fmt"
type Animal interface {
Speak()
}
type Dog struct {
}
func (d *Dog) Speak() {
fmt.Println("Ruff!")
}
func NewDog() *Dog {
return &Dog{}
}
func main() {
pets := make([]*Animal, 2)
pets[0] = NewDog()
(*pets[0]).Speak()
}
package main
import "fmt"
type Animal interface {
Speak()
}
type Dog struct {
}
func (d *Dog) Speak() {
fmt.Println("Ruff!")
}
func NewDog() *Dog {
return &Dog{}
}
func main() {
pets := make([]Animal, 2)
pets[0] = NewDog()
pets[0].Speak()
}
You don't need a Slice of pointers to Animal interfaces.
http://golang.org/doc/effective_go.html#pointers_vs_values
just change your code to:
func main() {
pets := make([]Animal, 2)
pets[0] = NewDog()
pets[0].Speak()
}
a interface value is already an implicit pointer.

Resources