CAP 05 · LEC 01·Composición de datos

Arrays y slices: capacidad, longitud y `append`

En Go, los arrays tienen tamaño fijo como parte del tipo y los slices son la abstracción dinámica que usarás el 99% del tiempo. Entender cómo se relacionan evita bugs sutiles con `append` y memoria compartida.

● INTERMEDIO8 min lecturapor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

Arrays: tamaño fijo como parte del tipo

Un array en Go tiene un tamaño fijo que forma parte del tipo. [5]int y [6]int son tipos distintos y no se pueden asignar entre sí. Por eso, en código real, casi nunca usarás arrays directamente.

package main import "fmt" func main() { // Array de 5 enteros — el tamaño es parte del tipo var numbers [5]int fmt.Println(numbers) // [0 0 0 0 0] — valores zero por defecto // Literal con valores iniciales primes := [5]int{2, 3, 5, 7, 11} fmt.Println(primes) // [2 3 5 7 11] fmt.Println(len(primes)) // 5 // Asignación: el array se COPIA entero, no se comparte copy := primes copy[0] = 99 fmt.Println(primes[0]) // 2 — el original no cambia fmt.Println(copy[0]) // 99 // [...] deja al compilador contar los elementos days := [...]string{"lun", "mar", "mié"} fmt.Println(len(days)) // 3 }
Salida[0 0 0 0 0] [2 3 5 7 11] 5 2 99 3
El tamaño es parte del tipo

Una función que recibe [5]int no acepta [6]int. Por eso los arrays son rígidos: para colecciones de tamaño variable, usa siempre slices.

Slices: una vista sobre un array

Un slice es una estructura ligera con tres campos: puntero al array subyacente, longitud (len) y capacidad (cap). Los slices se redimensionan con append y son el tipo idiomático para listas en Go.

package main import "fmt" func main() { // Slice literal — sin tamaño en el tipo primes := []int{2, 3, 5, 7, 11} fmt.Println(primes) // [2 3 5 7 11] fmt.Println(len(primes)) // 5 fmt.Println(cap(primes)) // 5 // make: crea un slice con longitud y capacidad explícitas buffer := make([]int, 3, 10) fmt.Println(buffer) // [0 0 0] fmt.Println(len(buffer)) // 3 fmt.Println(cap(buffer)) // 10 // Slice nil — válido y útil var empty []int fmt.Println(empty == nil) // true fmt.Println(len(empty)) // 0 — len y append funcionan en nil }
Salida[2 3 5 7 11] 5 5 [0 0 0] 3 10 true 0

append y crecimiento del slice

append añade elementos al final de un slice. Si la capacidad alcanza, escribe en el mismo array subyacente; si no, asigna un array nuevo más grande y copia los datos. Siempre debes reasignar el resultado.

package main import "fmt" func main() { nums := make([]int, 0, 4) fmt.Printf("inicio: len=%d cap=%d\n", len(nums), cap(nums)) for i := 1; i <= 6; i++ { nums = append(nums, i) fmt.Printf("tras %d: len=%d cap=%d\n", i, len(nums), cap(nums)) } fmt.Println(nums) // append acepta varios elementos more := append(nums, 7, 8, 9) fmt.Println(more) // Concatenar dos slices con ... a := []int{1, 2, 3} b := []int{4, 5, 6} combined := append(a, b...) fmt.Println(combined) }
Salidainicio: len=0 cap=4 tras 1: len=1 cap=4 tras 2: len=2 cap=4 tras 3: len=3 cap=4 tras 4: len=4 cap=4 tras 5: len=5 cap=8 tras 6: len=6 cap=8 [1 2 3 4 5 6] [1 2 3 4 5 6 7 8 9] [1 2 3 4 5 6]
Siempre reasigna el resultado de append

append(s, x) puede devolver un slice nuevo si reasigna memoria. Si escribes append(s, x) sin guardar el retorno, perderás los cambios. Lo idiomático es s = append(s, x).

Slicing y array subyacente compartido

s[a:b] crea un slice que comparte el array subyacente con s. Modificar uno afecta al otro mientras no se reasigne la memoria. Es la principal fuente de bugs sutiles con slices.

package main import "fmt" func main() { base := []int{10, 20, 30, 40, 50} // s apunta al mismo array que base, desde el índice 1 hasta el 3 (exclusivo) s := base[1:3] fmt.Println(s) // [20 30] fmt.Println(len(s)) // 2 fmt.Println(cap(s)) // 4 — desde el índice 1 hasta el final // Modificar s muta base s[0] = 999 fmt.Println(base) // [10 999 30 40 50] // append dentro de la capacidad también muta base s = append(s, 777) fmt.Println(s) // [999 30 777] fmt.Println(base) // [10 999 30 777 50] — ¡el 40 se sobreescribió! // Para aislar, copia con make + copy independent := make([]int, len(s)) copy(independent, s) independent[0] = -1 fmt.Println(s) // [999 30 777] — sin cambios fmt.Println(independent) // [-1 30 777] }
Salida[20 30] 2 4 [10 999 30 40 50] [999 30 777] [10 999 30 777 50] [999 30 777] [-1 30 777]
Usa copy para aislar slices

Cuando necesites un slice realmente independiente, usa dst := make([]T, len(src)); copy(dst, src). Evita compartir memoria por accidente entre funciones o estructuras.

Iterar slices con range

range recorre un slice devolviendo índice y valor. El valor es siempre una copia — modificarlo dentro del bucle no afecta al slice original.

package main import "fmt" func main() { words := []string{"go", "es", "simple"} // Índice y valor for i, w := range words { fmt.Printf("%d: %s\n", i, w) } // Solo índice (omitir valor) for i := range words { fmt.Println(i) } // Modificar el valor de range NO modifica el slice nums := []int{1, 2, 3} for _, n := range nums { n *= 10 // copia local — sin efecto } fmt.Println(nums) // [1 2 3] // Para modificar, usa el índice for i := range nums { nums[i] *= 10 } fmt.Println(nums) // [10 20 30] }
Salida0: go 1: es 2: simple 0 1 2 [1 2 3] [10 20 30]