8

For example I have list of functions that I want to compare:

http://play.golang.org/p/_rCys6rynf

type Action func(foo string)

type Handler struct {
  Get Action
  Post Action
}

var routes map[string]Handler

func Undefined(foo string) {
}

func Defined(foo string) {
}

func init() {
  routes = map[string]Handler{
    `/`: Handler{Defined,Undefined},
  }
}

func main() {
  for _, handler := range routes {
    if handler.Post != Undefined { 
      // do something
    } // invalid operation: (func(string))(handler.Post) != Undefined (func can only be compared to nil)


    if &handler.Post != &Undefined { 
      // do something 
    } // cannot take the address of Undefined
    // invalid operation: &handler.Post != &Undefined (mismatched types *Action and *func(string))
  }
}

What is the correct way to compare if two functions are the same?

icza
  • 389,944
  • 63
  • 907
  • 827
Kokizzu
  • 24,974
  • 37
  • 137
  • 233

3 Answers3

20

Before going further: you should refactor and not compare function value addresses.

Spec: Comparison operators:

Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil.

Function values are not comparable. What you may do is compare if the addresses of the function values are the same (not the address of variables holding function values, but the function values themselves).

You can't take the address of a function, but if you print it with the fmt package, it prints its address. So you can use fmt.Sprintf() to get the address of a function value.

See this example (based on your code):

hand := &Handler{Undefined, Defined}
p1 := fmt.Sprintf("%v", Undefined)
p2 := fmt.Sprintf("%v", hand.Get)
fmt.Println("Expecting true:", p1 == p2)

fmt.Println("Expecting false:", fmt.Sprintf("%v", Defined) == fmt.Sprintf("%v", hand.Get))
fmt.Println("Expecting true:", fmt.Sprintf("%v", Defined) == fmt.Sprintf("%v", hand.Post))

Output (try it on the Go Playground):

Expecting true: true
Expecting false: false
Expecting true: true

Another option would be to use reflect.Value.Pointer() to get the address of the function values, this is exactly what the fmt package does: fmt/print.go:

func (p *pp) fmtPointer(value reflect.Value, verb rune) {
    // ...
    case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice,
            reflect.UnsafePointer:
        u = value.Pointer()
    // ...
}

But you should refactor and not compare function value addresses.

icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    Can you elaborate on the pitfalls of comparing function addresses? Also, if the function has an address in memory, why is it not directly addressable? Can you tell me whether in your opinion, the string address value of a function is acceptable [for my use case](http://stackoverflow.com/q/42140758/1375316)? – trey-jones Feb 09 '17 at 18:08
  • @threeve Answered in your question: [Collection of Unique Functions in Go](http://stackoverflow.com/questions/42140758/collection-of-unique-functions-in-go/42147285#42147285) – icza Feb 09 '17 at 22:00
8

Nevermind, found the answer:

runtime.FuncForPC(reflect.ValueOf(handler.Post).Pointer()).Name() != 
   runtime.FuncForPC(reflect.ValueOf(Undefined).Pointer()).Name()
Kokizzu
  • 24,974
  • 37
  • 137
  • 233
0

You can't compare function. It need to be stored into a variable instead and reference it as pointer.

http://play.golang.org/p/sflsjjCHN5