GOOGLE ADS

miércoles, 4 de mayo de 2022

¿Por qué Go maneja los cierres de manera diferente en goroutines?

Considere el siguiente código Go (también en el Go Playground ):

package main
import "fmt"
import "time"
func main() {
for _, s:= range []string{"foo", "bar"} {
x:= s
func() {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}()
}
fmt.Println()
for _, s:= range []string{"foo", "bar"} {
x:= s
go func() {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}()
}
time.Sleep(time.Second)
}

Este código produce el siguiente resultado:

s: foo
x: foo
s: bar
x: bar
s: bar
x: foo
s: bar
x: bar

Asumiendo que esto no es un error extraño del compilador, tengo curiosidad por qué a) el valor de s se interpreta de manera diferente en la versión goroutine que en la llamada de función normal yb) y por qué asignarlo a una variable local dentro del ciclo funciona en ambos casos.


Solución del problema

Los cierres en Go tienen un alcance léxico. Esto significa que cualquier variable a la que se haga referencia dentro del cierre desde el ámbito "externo" no es una copia sino una referencia. Un forbucle en realidad reutiliza la misma variable varias veces, por lo que está introduciendo una condición de carrera entre la lectura/escritura de la svariable.

Pero xestá asignando una nueva variable (con :=) y copiando s, lo que da como resultado que ese sea el resultado correcto cada vez.

En general, es una buena práctica pasar cualquier argumento que desee para que no tenga referencias. Ejemplo:

for _, s:= range []string{"foo", "bar"} {
x:= s
go func(s string) {
fmt.Printf("s: %s\n", s)
fmt.Printf("x: %s\n", x)
}(s)
}

No hay comentarios.:

Publicar un comentario

Flutter: error de rango al acceder a la respuesta JSON

Estoy accediendo a una respuesta JSON con la siguiente estructura. { "fullName": "FirstName LastName", "listings...