Entrevista avanzada de Go: de gorutinas a genéricos
\n\nUna entrevista para un puesto de desarrollador Go de nivel medio o senior no es solo una verificación de sintaxis. Los empleadores esperan una comprensión profunda del modelo de concurrencia, el funcionamiento del runtime, la gestión de memoria y las particularidades del tipado. En este artículo, analizaremos las mejores preguntas avanzadas sobre Golang que te ayudarán a superar con confianza una entrevista técnica. No nos detendremos en conceptos básicos como "qué es un slice", sino que pasaremos directamente a temas complejos con ejemplos de código.
\n\n1. Gorutinas, canales y patrones de sincronización
\n\nLas preguntas sobre gorutinas y canales son clásicas en cualquier entrevista. Sin embargo, a nivel avanzado no solo te preguntarán "qué es una gorutina", sino también sobre los detalles del planificador (modelo GMP) y el uso correcto de los primitivos de sincronización.
\n\nPregunta: ¿Cómo funciona el planificador de gorutinas en Go? ¿Qué son G, M, P?
\nEl planificador de Go utiliza el modelo GMP: G (goroutine) — hilo ligero, M (machine) — hilo del SO, P (processor) — contexto de ejecución. P está vinculado a un procesador lógico y gestiona la cola de gorutinas. Cuando una gorutina se bloquea (por ejemplo, en una llamada al sistema), P puede cambiar M a otra gorutina, lo que proporciona una multitarea eficiente sin crear miles de hilos del SO.
Pregunta: ¿Cuál es la diferencia entre un canal con buffer y uno sin buffer? Proporcione un ejemplo de deadlock.
\n\n// Canal sin buffer: bloquea al emisor hasta que se recibe\nch := make(chan int)\ngo func() {\n ch <- 42 // se bloquea hasta que main lea\n}()\nfmt.Println(<-ch) // 42\n\n// Canal con buffer: no bloquea hasta que el buffer esté lleno\nchBuf := make(chan int, 2)\nchBuf <- 1\nchBuf <- 2 // ok, el buffer no está lleno\n// chBuf <- 3 // deadlock, si no hay lector\n\n// Deadlock típico: leer de un canal sin buffer vacío\nchDead := make(chan int)\n// <-chDead // fatal error: all goroutines are asleep — deadlock!\n\n\nPregunta: ¿Cómo cerrar un canal de forma segura? ¿Qué es "range over channel"?
\nSolo el emisor debe cerrar el canal. El receptor puede usar el segundo parámetro (v, ok := <-ch) para verificar. La iteración for v := range ch finalizará cuando el canal esté cerrado. Intentar escribir en un canal cerrado provoca un panic.
ch := make(chan int)\ngo func() {\n for i := 0; i < 3; i++ {\n ch <- i\n }\n close(ch) // obligatorio cerrar\n}()\nfor v := range ch {\n fmt.Println(v) // 0, 1, 2\n}\n\n\n2. Interfaces, interfaz vacía y type assertions
\n\nLas interfaces en Go son contratos. En una entrevista es importante entender cómo están estructuradas internamente (estructura iface y eface) y en qué se diferencia el tipado estático del dinámico.
Pregunta: ¿Qué es la interfaz vacía (interface{}) y cuándo usarla?
La interfaz vacía no contiene métodos, por lo que cualquier tipo la implementa. Se usa para trabajar con datos de tipo desconocido (por ejemplo, json.Unmarshal). Sin embargo, abusar de la interfaz vacía rompe el tipado estricto de Go y puede provocar errores en tiempo de ejecución.
Pregunta: ¿Cómo funciona type assertion? ¿Cuál es la diferencia con type switch?
\n\nvar val interface{} = "hello"\n\n// type assertion\nstr, ok := val.(string)\nif ok {\n fmt.Println("Esto es un string:", str)\n}\n\n// type switch\nswitch v := val.(type) {\ncase string:\n fmt.Println("String:", v)\ncase int:\n fmt.Println("Número:", v)\ndefault:\n fmt.Println("Tipo desconocido")\n}\n\n\nPregunta: ¿Cómo se almacenan las interfaces en memoria?
\nCada interfaz son dos punteros: uno al tipo (itable) y otro al valor (data). Para la interfaz vacía (interface{}) se