dictionary for-loop go closures slice

dictionary - ¿Por qué estas dos variaciones de bucle me dan un comportamiento diferente?



for-loop go (1)

Veo un comportamiento diferente en mi programa que está vinculado a este ciclo particular en mi programa, pero no estoy seguro de entender por qué se comporta de la manera que es.

//global variable var cmds = []string { "create", "delete", "update", } func loop1() { actions := make(map[string]func()) for _, cmd := range cmds { actions[cmd] = func() { fmt.Println(cmd) } } for _, action := range actions { action() } } func loop2() { actions := make(map[string]func()) for i, cmd := range cmds { command := cmds[i] actions[cmd] = func() { fmt.Println(command) } } for _, action := range actions { action() } }

La salida para loop1() es

update update update

La salida para loop2() es

delete update create

Fui a buscar en internet y leí lo siguiente

Cuando se extiende sobre un segmento, se devuelven dos valores para cada iteración. El primero es el índice, y el segundo es una copia del elemento en ese índice.

Dice una copia, entonces, ¿eso significa que devuelve una copia de la cadena pero realmente es un puntero a cmd variable? En cuyo caso, cualquier referencia a cmd al final del ciclo hará referencia al último elemento de la matriz, por ejemplo, ¿ update ? ¿Significa esto que los elementos de una matriz siempre deben ser referenciados por su índice cuando se usa el método de range , y cuál es el caso de uso para usar el elemento que devuelve ya que siempre está actualizando el puntero?


El problema con loop1() es que almacena una función literal en el mapa de actions que hace referencia a la variable de bucle cmd . Solo hay una instancia de esta variable de bucle, por lo que cuando después del bucle llame a las funciones almacenadas en el mapa de actions , todas se referirán a esta variable de bucle único (que se mantiene porque las funciones / cierres todavía tienen una referencia), pero su valor en el momento de la ejecución será el último valor establecido por el bucle for , que es el último valor en el segmento de cmds (es decir, "update" , por lo que verá "update" impreso 3 veces).

Una solución fácil es hacer una copia de esta variable de bucle, por lo que cada iteración, cada literal de función tendrá su propia copia, que está "separada" de la variable de bucle:

func loop1() { actions := make(map[string]func()) for _, cmd := range cmds { cmd2 := cmd actions[cmd] = func() { fmt.Println(cmd2) // Refer to the detached, copy variable! } } for _, action := range actions { action() } }

Con esto, salida de loop1() (pruébalo en Go Playground ):

update create delete

Este no es un problema del for ... range , es porque los cierres se refieren a la misma variable, y no usas el valor de la variable de inmediato, solo después del ciclo. Y cuando imprime el valor de esta variable, todos imprimen el mismo último valor.

También vea este posible duplicado: Golang: Registre múltiples rutas usando el rango para segmentos / mapa de bucle