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

南笙酒味 提交于 2019-12-17 09:21:20

问题


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

回答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
}



回答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.




回答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?)



来源:https://stackoverflow.com/questions/9643205/how-do-i-compare-two-functions-for-pointer-equality-in-the-latest-go-weekly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!