ProgramaciónDesarrollador Backend

Describa las características del trabajo con slices en Go: ¿qué es un nil slice, cuál es la diferencia entre length y capacity, y cómo aumentar correctamente un slice sin fugas de datos o pánicos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Un slice es un array dinámico, la unidad estructural principal al trabajar con arrays en Go. Históricamente, los lenguajes de programación ofrecían arrays fijos o estructuras más pesadas. Go implementó slices como una herramienta conveniente para manejar colecciones de memoria con gestión automática de su tamaño y la capacidad de cambiar la longitud.

El problema radica en la gestión adecuada de la memoria, manejo de casos límite (slices vacíos, nil, llenos) y en entender la diferencia entre la longitud y la capacidad de un slice.

La solución es utilizar correctamente las funciones integradas len(), cap(), y operar con slices de acuerdo con las convenciones de Go.

Ejemplo de código:

var a []int // nil slice, len=0, cap=0 b := make([]int, 0) // slice vacío, len=0, cap=0 c := make([]int, 3, 5) // len=3, cap=5 c = append(c, 4, 5, 6) // cap se incrementará automáticamente

Características clave:

  • Nil slice (var s []int) no asigna memoria en absoluto, se diferencia de un slice vacío (make([]int, 0)) solo internamente.
  • Length muestra el número actual de elementos, capacity — el máximo sin asignación adicional.
  • Al hacer append, si la capacidad no es suficiente, se crea un nuevo bajo el capó, y los datos antiguos permanecen sin cambios.

Preguntas engañosas.

¿Qué longitud y capacidad tendrá un slice después de append a un nil slice?

El nil slice (var s []int) después de append(s, 1) se convierte en un slice de longitud 1, capacidad 1 — Go maneja la asignación de memoria automáticamente.

var s []int s = append(s, 42) // s ahora es [42], len=1, cap=1

¿Se puede acceder a la capacity de un slice que es nil?

Sí, en el nil slice ambas funciones — len y cap — devuelven 0. No habrá pánico.

var s []int fmt.Println(len(s), cap(s)) // 0 0

¿Qué sucederá si intento asignar a un elemento por índice sin asignar memoria previamente a un slice que es nil?

Pánico index out of range, porque el slice no tiene elementos.

var s []int s[0] = 1 // pánico: error de tiempo de ejecución: índice fuera de rango

Errores comunes y anti-patrón

  • Errores al pasar un nil slice o un slice vacío a una función donde se requiere una longitud específica.
  • Sobrescribir el slice sin tener en cuenta el crecimiento de la capacity y ciclos escritos de manera ilógica con append.
  • Intentos de asignación por índice en lugar de append para agregar elementos.

Ejemplo de la vida real

Caso negativo

El equipo decidió usar únicamente var s []T en el servidor Go, esperando que esto siempre fuera equivalente a make([]T, 0). Como resultado, algunas marshaling de json devolvían null en lugar de [].

Ventajas:

  • Menos asignaciones de memoria al inicio.

Desventajas:

  • Funcionamiento incorrecto de JSON (resultando en null en lugar de un array).
  • Errores de tiempo de ejecución al acceder por índice.

Caso positivo

Uso de make([]T, 0, 100) para preasignación, y luego solo append en el ciclo. Esto minimiza las asignaciones de memoria y a menudo se traduce en un rendimiento mejorado.

Ventajas:

  • Trabajo eficiente con la memoria.
  • No hay panics ni valores inesperados al marshalling.

Desventajas:

  • Posible sobre-consumo de memoria si el número esperado de elementos no coincide con la capacity dada.