JG
Volver al blog

Go: La diferencia sutil entre 'existir' y 'hacer' (make)

Cuando estaba aprendiendo Go (Golang) y, como buen desarrollador fullstack que viene de lenguajes más permisivos, me topé con una pared curiosa. Escribí un código que funcionaba perfectamente usando literales {}, y luego vi que la documentación oficial insistía en usar una función llamada make().

Mi primera reacción fue: “Si ya funciona, ¿para qué complicarlo?”.

Pero luego recordé esa sensación de cuando copias código de Stack Overflow (o de ChatGPT) sin entender qué hace por debajo. Funciona, sí, pero es como construir una casa sobre arena. Así que decidí investigar a fondo. Resulta que make no es un capricho sintáctico, es la diferencia entre declarar que algo existe y prepararlo para la guerra.

Es decir que, es la diferencia entre declarar algo que simplemente funcione a nivel de sintaxis y declarar algo diseñado para soportar la carga y optimizar el rendimiento de nuestros programas. La diferencia entre un programador que resuelve o uno que piensa a futuro y optimiza.

Dr Stone Science
Debes entender al 1000 millones por ciento como funciona la memoria para que puedas optimizar tu código.

El caso del Mapa Fantasma

Vamos al grano. Comencemos con los MAP o Diccionarios. Mira este código que funciona bien:

// Esto funciona y es válido
version1 := map[string]int{
    "Jose":  4,
    "Maria": 5,
}

Es limpio, es bonito. Pero, ¿qué pasa si declaras el mapa pero no lo inicializas?

var mapa map[string]int
// mapa["Jose"] = 10 
//ESTO VA A EXPLOTAR (PANIC)

Aquí es donde entra la magia. En Go, un mapa declarado sin inicializar es nil. No existe en memoria. Si intentas escribir en él, tu programa entra en pánico (literalmente, panic: assignment to entry in nil map).

Esto me trae terribles y traumáticos recuerdos de cuando estaba aprendiendo a programar con Java. El famoso y odiado error NullPointerException que no me dejaba dormir por las noches y me hacía despertarme a medias de la noche bañado en sudor.

Por suerte que en Go ya tienen un sistema para evitar esto, con make.

make no solo declara la variable, sino que inicializa la estructura de datos interna (el hashmap) para que esté lista para recibir datos.

NOTA: Si sabes exactamente qué datos vas a meter desde el principio, usa el literal {}. Pero si vas a llenar el mapa dinámicamente dentro de un bucle o condicionales, make es tu mejor amigo para evitar errores de nil.

Slices: No es solo el tamaño, es la capacidad

Donde make realmente brilla y separa a los juniors de nosotros los seniors 😎 es en los Slices.

Un slice en Go es una ventana a un array subyacente. Cuando usas make con slices, puedes controlar dos cosas: el tamaño (len) y la capacidad (cap).

// make([]tipo, longitud, capacidad)
slice := make([]int, 0, 100)

¿Por qué importa esto? Imagina que estás metiendo 1,000 elementos en una lista.

  1. Si no defines capacidad, Go crea un array pequeño por defecto, esto parece ser bueno a nivel de memoria.
  2. Se llena, elemento por elementos, así que el array se va creciendo y creciendo.
  3. Go tiene que pausar, buscar un espacio de memoria más grande, copiar todo lo viejo al nuevo lugar y borrar lo viejo.
  4. Repite esto muchas veces (ya no suena tan bonito ¿cierto?).

Eso es costoso a nivel de CPU. Con make, le dices a Go: “Oye, resérvame memoria para 100 elementos de un solo golpe”. El programa vuela porque no pierde tiempo buscando memoria nueva a cada rato.

Memory Allocation
Solo de pensar en la memoria RAM ya soltó una lágrima mi billetera.

Channels: El controlador de tráfico

Finalmente, el tercer uso de make es para la característica estrella de Go: los Canales (Channels). Aquí make define si la comunicación es síncrona o asíncrona.

Tranquilo, si no sabes qué es un canal en Go, no te preocupes. Solo es una forma de comunicación entre goroutines (Creo que te dejé más confundido con esto, mejor escribiré un artículo sobre esto luego).

// Canal sin búfer (Síncrono)
ch := make(chan int) 

// Canal con búfer (Asíncrono)
ch := make(chan int, 5)
  • Sin búfer: Es como una llamada telefónica. Yo hablo, tú escuchas. Si tú no estás al otro lado, yo me quedo esperando (bloqueado).
  • Con búfer: Es como un mensaje de voz o email. Yo dejo 5 mensajes en el buzón y sigo con mi vida. Tú los escuchas cuando puedas.

Sin make, no puedes crear canales utilizables. Aquí no hay “literal” que te salve.

Conclusión

Volviendo a la reflexión de mi post anterior: la IA puede generarte el código con make o con literales indistintamente, pero eres quien debe saber cuál usar para que el sistema escale.

También puede interesarte
Portada del artículo

Si la IA conmigo, ¿quién contra mí? ¿verdad?

Usar make correctamente es decirle a tu programa: “Sé lo que voy a necesitar, prepárate”.

  • Úsalo en Maps para evitar pánicos y optimizar si sabes el tamaño.
  • Úsalo en Slices para evitar realocaciones de memoria costosas.
  • Úsalo en Channels para controlar la concurrencia.

En resumen: Programar en Go sin entender make es como querer conducir un Fórmula 1 en primera. Funciona, pero te estás perdiendo toda la potencia. Aprovecha al máximo el lenguaje.