Introducción: ¿Por qué Go y por qué es tan difícil?
\n\nGo (o Golang) es un lenguaje que se adora por su simplicidad y se odia por la misma simplicidad. En las entrevistas de Go rara vez preguntan "escribe un ordenamiento de burbuja" (aunque también ocurre). La mayoría de las veces te encontrarás con problemas de concurrencia, comprensión de la gestión de memoria y la capacidad de escribir código limpio e idiomático. Si estás acostumbrado a Python o Java, Go puede parecer demasiado estricto. Pero es precisamente esta rigurosidad lo que lo convierte en el lenguaje ideal para sistemas en producción.
\n\nEn este artículo, analizaremos los temas clave que seguramente surgirán en la entrevista. No obtendrás solo teoría, sino ejemplos concretos de código y una explicación "bajo el capó". ¡Allá vamos!
\n\n1. Gorutinas y canales: conceptos básicos que debes saber de memoria
\n\nLas gorutinas no son hilos
\nLa pregunta más frecuente: "¿En qué se diferencia una gorutina de un hilo del SO?". Respuesta: una gorutina es liviana (pila desde 2 KB), gestionada por el planificador de Go, no por el núcleo. Un millón de gorutinas es normal. Un millón de hilos mataría el sistema.
\n\n// Ejemplo: lanzar 10 gorutinas\nfor i := 0; i < 10; i++ {\n go func(id int) {\n fmt.Printf("Gorutina %d funcionando\\n", id)\n }(i)\n}\n// Esperamos (mala práctica, solo para el ejemplo)\ntime.Sleep(time.Second)\n\n\nCanales: sincronización sin dolor
\nLos canales en Go son "tuberías" tipadas para transferir datos entre gorutinas. Recuerda tres reglas:
\n- \n
- Escribir en un canal cerrado provoca pánico. \n
- Leer de un canal cerrado devuelve el valor cero. \n
- Un canal sin búfer bloquea la gorutina hasta que aparezca un receptor. \n
// Trabajo correcto con canales\nch := make(chan int, 2) // con búfer\n\nch <- 1\nch <- 2\nclose(ch)\n\nfor val := range ch {\n fmt.Println(val) // 1, 2\n}\n\n\nConsejo para la entrevista: si preguntan sobre "select", muestra un ejemplo con tiempo de espera:
\n\nselect {\ncase msg := <-ch:\n fmt.Println(msg)\ncase <-time.After(1 * time.Second):\n fmt.Println("¡Tiempo de espera agotado!")\n}\n\n\n2. Interfaces: no solo métodos, sino contratos
\n\nEn Go, las interfaces se implementan implícitamente. Esto significa que una estructura satisface automáticamente una interfaz si tiene todos los métodos necesarios. A menudo preguntan: "¿Cuál es la diferencia entre interface{} y any?" — respuesta: ninguna, son sinónimos (desde Go 1.18).
Problema típico en la entrevista
\nEscribe una función que acepte io.Writer y escriba una cadena en él. Esto evalúa la comprensión de las interfaces de la biblioteca estándar.
func WriteHello(w io.Writer) {\n _, err := w.Write([]byte("¡Hola, Go!"))\n if err != nil {\n log.Fatal(err)\n }\n}\n\n// Uso:\nvar buf bytes.Buffer\nWriteHello(&buf)\nfmt.Println(buf.String()) // ¡Hola, Go!\n\n\nTrampa: todas las interfaces en Go son dos punteros (tipo + valor). Una interfaz vacía (interface{}) no es igual a nil si contiene (*int)(nil). Recuérdalo para no fallar en la pregunta sobre nil != nil.
3. Genéricos: nuevo, pero ya obligatorio
\n\nDesde Go 1.18, los genéricos son parte del lenguaje. En la entrevista pueden pedirte que escribas una función genérica. Por ejemplo, una función que acepte un slice de cualquier tipo y devuelva su longitud (trivial) o encuentre el máximo.
\n\n// Función genérica para encontrar el máximo\nfunc Max[T constraints.Ordered](a, b T) T {\n if a > b {\n return a\n }\n return b\n}\n\n// Uso:\nfmt.Println(Max(10, 20)) // 20\nfmt.Println(Max(3.14, 2.71)) // 3.14\n\n\nObserva el paquete constraints — se usa a menudo en preguntas. Pero recuerda: desde Go 1.21 fue reemplazado por el incorporado cmp.Ordered.
4. Manejo de errores: no los ignores
\n\nEn Go no hay try-catch. Los errores son valores.