¿Cómo comparo dos funciones para la igualdad del puntero en el último Go weekly?
function-pointers go-reflect (3)
Las comparaciones entre el mapa y el valor de la función ahora no están permitidas (excepto para la comparación con cero) según el plan Go 1. La igualdad de funciones era problemática en algunos contextos y la igualdad de mapas compara punteros, no el contenido de los mapas.
La igualdad de funciones era problemática en presencia de cierres (¿cuándo son iguales dos cierres?)
En Go, ¿hay alguna forma de comparar dos punteros de función que no sean nulos para probar la igualdad? Mi estándar de igualdad es la igualdad del puntero. Si no, ¿hay alguna razón particular por la cual la igualdad del puntero no está permitida?
A partir de ahora, si intento hacer esto de la manera más directa:
package main
import "fmt"
func SomeFun() {
}
func main() {
fmt.Println(SomeFun == SomeFun)
}
yo obtengo
./func-pointers.go:12: invalid operation: SomeFun == SomeFun (func can only be compared to nil)
Tengo entendido que este comportamiento fue introducido recientemente.
Encontré una respuesta usando el paquete de reflejar; sin embargo, Atom sugiere a continuación que esto realmente produce un comportamiento indefinido. Consulte la publicación de Atom para obtener más información y una posible solución alternativa.
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())
}
Productos:
true
false
La solución depende de la situación. Tuve que cambiar un par de lugares donde estaba comparando funciones. En una ocasión, hice algo diferente para no tener que compararlos más. En otro caso, utilicé una estructura para asociar funciones con cadenas comparables, algo así como,
type nameFunc struct {
name string
fval func()
}
Solo tenía un par de funciones que necesitaba comparar, por lo que era más simple guardar una porción de estas estructuras y escanear el sector según fuera necesario, comparando el campo de nombre y enviando fval. Si tiene muchos, puede usar un mapa en su lugar. Si sus funciones tienen diferentes firmas, puede usar interfaces, y así sucesivamente.
Tenga en cuenta que hay una diferencia entre igualdad e identidad. Los operadores ==
y !=
En Go1 están comparando los valores para la equivalencia (excepto cuando se comparan canales), no para la identidad. Debido a que estos operadores están tratando de no mezclar igualdad e identidad, Go1 es más consistente que pre-Go1 en este aspecto.
La igualdad de funciones es diferente de la identidad de la función.
Una razón para no permitir ==
y !=
En los tipos de funciones es el rendimiento. Por ejemplo, el siguiente cierre no utiliza variables de su entorno:
f := func(){fmt.Println("foo")}
La deshabilitación de las comparaciones de funciones permite al compilador generar una única implementación para el cierre, en lugar de requerir que el tiempo de ejecución cree un nuevo cierre (en tiempo de ejecución). Entonces, desde el punto de vista del desempeño, la decisión de no permitir las comparaciones de funciones fue una buena decisión.
En relación con el uso del paquete de reflect
para determinar la identidad de la función, un código como
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
}
se basa en un comportamiento indefinido . No hay garantías en cuanto a lo que imprimirá el programa. El compilador puede decidir que fusionará SomeFun
y AnotherFun
en una sola implementación, en cuyo caso la segunda declaración de impresión se imprimiría true
. De hecho, no hay absolutamente ninguna garantía de que la primera declaración de impresión se imprimirá true
(puede, bajo otro compilador Go1 y tiempo de ejecución, imprimir false
).
Una respuesta correcta a su pregunta original es:
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
}