How do I compare two functions for pointer equality in the latest Go weekly?

20,322

Solution 1

Note that there is a difference between equality and identity. The operators == and != in Go1 are comparing the values for equivalence (except when comparing channels), not for identity. Because these operators are trying not to mix equality and identity, Go1 is more consistent than pre-Go1 in this respect.

Function equality is different from function identity.


One reason for not allowing == and != on function types is performance. For example, the following closure is not using any variables from its environment:

f := func(){fmt.Println("foo")}

Disallowing comparisons of functions enables the compiler to generate a single implementation for the closure, instead of requiring the run-time to create a new closure (at run-time). So, from performance viewpoint the decision to disallow function comparisons was a good decision.


In relation to using the reflect package to determine function identity, a code like

func SomeFun()    {}
func AnotherFun() {}

func main() {
    sf1 := reflect.ValueOf(SomeFun)
    sf2 := reflect.ValueOf(SomeFun)
    fmt.Println(sf1.Pointer() == sf2.Pointer())  // Prints true

    af1 := reflect.ValueOf(AnotherFun)
    fmt.Println(sf1.Pointer() == af1.Pointer())  // Prints false
}

relies on undefined behavior. There are no guarantees as to what the program will print. The compiler may decide that it will merge SomeFun and AnotherFun into a single implementation, in which case the 2nd print statement would print true. In fact, there is absolutely no guarantee that the 1st print statement will print true (it may, under some other Go1 compiler and run-time, print false).


A correct answer to your original question is:

package main

import "fmt"

func F1() {}
func F2() {}

var F1_ID = F1  // Create a *unique* variable for F1
var F2_ID = F2  // Create a *unique* variable for F2

func main() {
    f1 := &F1_ID  // Take the address of F1_ID
    f2 := &F2_ID  // Take the address of F2_ID

    // Compare pointers
    fmt.Println(f1 == f1)  // Prints true
    fmt.Println(f1 == f2)  // Prints false
}

Solution 2

The workaround depends on the situtation. I had to change a couple of places where I was comparing functions. In once case I just did something different so I wouldn't need to compare them any more. In another case I used a struct to associate functions with comparable strings, something like,

type nameFunc struct {
    name string
    fval func()
}

I only had a couple of functions I needed to compare so it was simplest to keep a slice of these structs and scan the slice as needed, comparing the name field and dispatching fval. If you have very many you might use a map instead. If your functions have different signatures you could use interfaces, and so on.

Solution 3

weekly.2011-11-18

Map and function value comparisons are now disallowed (except for comparison with nil) as per the Go 1 plan. Function equality was problematic in some contexts and map equality compares pointers, not the maps' content.

Equality

Function equality was problematic in the presence of closures (when are two closures equal?)

Share:
20,322
BurntSushi5
Author by

BurntSushi5

Updated on August 22, 2020

Comments

  • BurntSushi5
    BurntSushi5 almost 4 years

    In Go, is there any way to compare two non-nil function pointers to test for equality? My standard of equality is pointer equality. If not, is there any particular reason why pointer equality is not allowed?

    As of now, if I attempt to do this in the straight-forward way:

    package main
    
    import "fmt"
    
    func SomeFun() {
    }
    
    func main() {
        fmt.Println(SomeFun == SomeFun)
    }
    

    I get

    ./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil)
    

    It is my understanding that this behavior was introduced recently.


    I've found an answer using the reflect package; however Atom suggests below that this actually produces undefined behavior. See Atom's post for more info and a possible alternative solution.

    package main
    
    import "fmt"
    import "reflect"
    
    func SomeFun() { }
    
    func AnotherFun() { }
    
    func main() {
        sf1 := reflect.ValueOf(SomeFun)
        sf2 := reflect.ValueOf(SomeFun)
        fmt.Println(sf1.Pointer() == sf2.Pointer())
    
        af1 := reflect.ValueOf(AnotherFun)
        fmt.Println(sf1.Pointer() == af1.Pointer())
    }
    

    Outputs:

    true
    false
    
  • BurntSushi5
    BurntSushi5 over 12 years
    I don't think this really answers my question. I know that function value comparisons aren't allowed in the manner I am using; what I want to know is if there is any workaround. I'd also like to know why function pointer equality is problematic.
  • tchrist
    tchrist over 12 years
    @BurntSushi5 This is a good question. Certainly there are languages that do have closures where you can compare function pointers. Closures of the same function that are closed over different variables do not test equal in those languages. It seems like this is just dodging a representation issue. There is no fundamental underlying reason why this is disallowed in Go. It could be implemented in a sensible, reliable, and consistent way; they’ve simply chosen not to do so.
  • BurntSushi5
    BurntSushi5 over 12 years
    @peterSO In response to your edit: That doesn't really answer why function pointer equality can't be used. If two function pointers point to the same memory location, then they should be equal. It won't be quite as useful as a general form of function equality, but wouldn't be useless either.
  • BurntSushi5
    BurntSushi5 over 12 years
    @tchrist I totally understand. However, what I'm asking is pointer equality. There shouldn't be any nastiness with closures there; either two function pointers point to the same memory location or not. It isn't as sophisticated as what you're suggesting, but it's something.
  • BurntSushi5
    BurntSushi5 over 12 years
    Ah nice. I've also worked around it by just changing my approach. Thanks for the tip about keeping a string representation of a function. It won't work well for what I'm doing (allowing arbitrary callback functions in a library), but it may be useful down the line.
  • BurntSushi5
    BurntSushi5 over 12 years
    The reflect package has what I was looking for. I've updated my OP with the answer.
  • BurntSushi5
    BurntSushi5 over 12 years
    The reflect package has what I was looking for. I've updated my OP with the answer.
  • BurntSushi5
    BurntSushi5 over 12 years
    Excellent response. Thank you! You definitely get credit for answering "why", but I'm confused about your answer to my original question. It appears that it is testing the identity of the variables F1_ID and F2_ID rather than the identity of the functions F1 and F2. For example, if I had 'var F1_ID2 = F1', then &F1_ID == &F1_ID2 would return true if we were testing function identity; but it returns false.
  • BurntSushi5
    BurntSushi5 over 12 years
    Also, your critique of my approach using reflect worries me. Aren't you suggesting that testing function identity is impossible to guarantee in Go?
  • Admin
    Admin over 12 years
    Comment1: The assumption in the last code snippet is that a particular function has a single ID.
  • Admin
    Admin over 12 years
    Comment2: The reflect package isn't part of the language specification, so using it cannot guarantee function identity - but it is true that the Pointer() method works perfectly in the current implementation. ` ` Functions in Go1 do not have identity, and so the compiler is not obliged to track function identity. What I am saying is that the language specification implies that if the programmer needs to compare functions for identity it is left to the programmer to implement it. That said, I am not against using the reflect package if the programmer knows its limitations.
  • newacct
    newacct over 12 years
    @BurntSushi5: There are no "function pointers" per se. Every function value is (potentially) a closure. Two instances of the same closure (i.e. created by the same function expression in the code) would technically share the same underlying implementing code; so under your definition they should be "equal"? But since they have different values for the variables they close over, these two functions would behave differently. Can you still say they're "equal"?
  • tmm1
    tmm1 about 5 years
    Do F1_ID and F2_ID need to be globals for this to work?