Historia del tema:
Las rebanadas (slices) son una de las estructuras dinámicas clave en Go, que surgió como una alternativa a los arreglos de longitud fija para mejorar la conveniencia y la rentabilidad de la memoria. Proporcionan un manejo flexible de subconjuntos de arreglos, pero presentan una serie de sutilezas importantes para un código eficiente y seguro.
Problema:
Muchos desarrolladores no comprenden cómo están estructuradas las rebanadas: una slice no es el propio arreglo, sino una estructura con un puntero al arreglo, longitud y capacidad (capacity). Esto puede conducir a fugas de memoria, errores al trabajar con copias y efectos inesperados al modificar el arreglo original.
Solución:
Slice es un tipo:
type slice struct { ptr unsafe.Pointer len int cap int }
Al expandir una slice usando append(), puede producirse una redistribución del backing array, y todas las referencias anteriores al arreglo antiguo seguirán siendo válidas, pero apuntarán a los datos antiguos. No conocer esta particularidad puede causar errores y fugas de memoria.
Ejemplo de una asignación de memoria y copia correctas:
src := []int{1,2,3,4,5} dst := make([]int, len(src)) copy(dst, src)
Una slice creada con [:] comparte el underlying array, y su modificación afecta a otros, a menos que se realice una copia.
Características clave:
¿Qué ocurrirá al aumentar la slice a través de append superando la capacidad, si otras slices tienen referencias al mismo arreglo?
append, al exceder la capacidad, crea un underlying array en una nueva ubicación de memoria, y solo esta slice apunta al nuevo arreglo, mientras que las demás apuntan al antiguo. Esta es una causa común de discrepancia de datos.
¿Por qué es importante no mantener slices de pequeño tamaño que viven mucho tiempo, generadas desde un gran arreglo?
Incluso si la slice es muy pequeña, su puntero mantiene una referencia a todo el backing array, lo que puede llevar a mantener un gran arreglo en memoria y causar fugas de memoria.
¿Qué pasará si se hace una slice de un arreglo fuera de sus límites?
Se producirá un panic: error de tiempo de ejecución: los límites de la slice están fuera de rango.
La función lee un gran archivo en un arreglo de bytes y devuelve una slice de los primeros 100 elementos. Esta slice se almacena durante mucho tiempo, pero toda la memoria del gran arreglo permanece en GC.
Ventajas:
Desventajas:
Justo después de obtener la slice se copia la parte necesaria a una nueva slice con make y copy. El arreglo antiguo se olvida de inmediato, y el GC libera la memoria.
Ventajas:
Desventajas: