ProgramaciónDesarrollador Backend

¿En qué se diferencian la transmisión por referencia y por valor en Go? ¿Por qué este momento es crítico para las estructuras y los slices?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

En Go, por defecto, todos los argumentos de las funciones se pasan por valor: se copia el valor de la variable. Pero algunos tipos (como slices, maps, canales) son "envolturas" sobre estructuras internas (punteros). Pasar un slice por valor solo copia el descriptor del slice, no los datos; ambas variables apuntan al mismo arreglo. En el caso de las estructuras, se copia toda la estructura.

Si se requiere evitar la copia y trabajar con la estructura original, se utiliza la transmisión por puntero (*Struct).

Ejemplo:

type User struct { Name string Age int } func updateUser(u User) { u.Age = 30 // solo cambia la copia } func updateUserPtr(u *User) { u.Age = 30 // cambia el original } func main() { u := User{"Ivan", 25} updateUser(u) fmt.Println(u.Age) // 25 updateUserPtr(&u) fmt.Println(u.Age) // 30 }

Pregunta capciosa

¿Son siempre visibles los cambios en un slice pasado a la función desde afuera de la función?

¡No!

  • Si se cambia el contenido del slice (slice[i] = ...), es visible desde afuera.
  • Si se cambia el propio slice (por ejemplo, slice = append(slice, ...)), y el resultado no se devuelve de la función, los nuevos elementos estarán en una copia local y los perderás.

Ejemplo:

func addElem(s []int) { s = append(s, 100) } func main() { arr := []int{1,2,3} addElem(arr) fmt.Println(arr) // [1 2 3] — 100 no se añadió }

Ejemplos de errores reales debido a la ignorancia de los matices del tema


Historia

En uno de los proyectos, estructuras de datos con un campo struct grande (más de 200 bytes) se pasaban por valor a través de canales entre goroutines, lo que causaba enormes sobrecostos por copia y pérdidas de rendimiento. Después de cambiar a la transmisión por puntero, la latencia se redujo drásticamente.


Historia

En el servicio de registro de auditoría, un desarrollador pasaba un mapa (map) entre funciones sin clonado explícito (copy). Los cambios realizados por una función inesperadamente alteraban los datos en otra parte del programa, causando confusión en el registro.


Historia

En la función de aumento dinámico del slice dentro de la función, se olvidó devolver el nuevo slice. Como resultado, los cambios no se reflejaron en el código que lo llamó, lo que llevó a la pérdida de parte de las transacciones. Se decidió devolver el nuevo slice de la función.