🏠 Regresar

1. Fundamentos de Go (120 preguntas)

Sintaxis y semántica (30 preguntas)

1. ¿Cuál es el valor cero de un mapa en Go?

Por qué:

Comprender los valores cero evita problemas de ejecución causados por mapas no inicializados.

Qué:

  • El valor cero de un mapa es nil.
  • Un mapa nulo no puede contener pares clave-valor.

Cómo:


        	var m map[string]int // m == nil
        	m = make(map[string]int) // inicializar
        	m["age"] = 30 // ahora funciona
      	

Caso de uso en la vida real:

Un mapa nulo podría representar un objeto de configuración sin inicializar. Una inicialización correcta evita fallos al agregar configuraciones.


2. ¿Cómo maneja Go la conversión de tipo implícita entre int y float64?

Por qué:

Go aplica una tipificación estricta para evitar la pérdida accidental de datos.

Qué:

  • No hay conversión implícita entre tipos numéricos.
  • Se requiere casting explícito.

Cómo:


          i := 42
          f := float64(i) // conversión explícita
          i = int(f) // volver a int (trunca el decimal)
        

Caso de uso en la vida real:

Conversión de lecturas de sensores (enteros) a valores de punto flotante para cálculos científicos.


3. Escribe el código para declarar constantes para los días de la semana usando iota.

Por qué:

iota simplifica las declaraciones de constantes secuenciales.

Qué:

  • iota se incrementa automáticamente desde 0 en un bloque de constantes.

Cómo:


          const(
          Sunday = iota  // 0
          Monday         // 1
          Tuesday        // 2
          // ... hasta Saturday (6))
        

Caso de uso en la vida real:

Representar días en sistemas de programación (por ejemplo, aplicaciones de calendario).


4. Explica la diferencia entre var x int = 10 y x := 10.

Por qué:

Elegir el estilo de declaración correcto afecta la legibilidad y el alcance del código.

Qué:

  • var x int = 10: Declaración de tipo explícita, funciona en cualquier ámbito.
  • x := 10: Inferencia de tipos, solo dentro de funciones.

Cómo:


          varglobalVar int = 10 // nivel de paquete

          funcmain(){
            localVar := 20 // nivel de función
          }
        

Caso de uso en la vida real:

Usando var para constantes a nivel de paquete y := para variables de bucle local.


5. ¿Por qué Go no permite variables o importaciones no utilizadas?

Por qué:

Go prioriza el código limpio y eficiente y la seguridad en tiempo de compilación.

Qué:

  • Las variables/importaciones no utilizadas indican posibles errores o inflación.
  • El compilador lanza errores para forzar la limpieza.

Cómo:


          // Error: unused import "fmt"
          // Error: unused variable x
          import"fmt"
          func main() {
            x := 10
          }
        

Caso de uso en la vida real:

Evitar que las variables de depuración sobrantes se envíen a producción.


6. ¿Cómo se crea una cadena de varias líneas sin caracteres de escape?

Por qué:

Las cadenas sin formato mejoran la legibilidad de las plantillas o JSON.

Qué:

  • Utilice comillas invertidas ` para literales de cadena sin formato.

Cómo:


          s :=`Line 1
          Line 2
          Line 3`
        

Caso de uso en la vida real:

Incrustar consultas SQL o plantillas HTML en el código.


7. ¿Cuál es el propósito del _ (identificador en blanco) en los bucles for?

Por qué:

Ignora los valores que no necesitas, evitando las advertencias del compilador.

Qué:

  • Omite el índice o valor en bucles de rango.

Cómo:


          for _, value := range []int{1,2,3} {
            fmt.Println(value) // ignorar indice
          }
        

Caso de uso en la vida real:

Procesamiento de elementos de slices sin necesidad de conocer sus posiciones.


8. ¿Cómo maneja Go el desbordamiento de int8?

Por qué:

El manejo de desbordamientos afecta la integridad de los datos en sistemas de bajo nivel.

Qué:

  • Se envuelve usando aritmética de complemento a dos.

Cómo:


          var a int8 = 127
          a++ // -128 (desbordamiento)
        

Caso de uso en la vida real:

Manejo de datos de sensores que exceden los límites del hardware.


9. ¿Cuál es la salida de fmt.Printf("%T", 'a')?

Por qué:

Comprender el tipo de runa de Go es clave para el manejo de Unicode.

Qué:

  • 'a' es una runa (alias para int32).

Cómo:


          fmt.Printf("%T",'a') // salida: int32
        

Caso de uso en la vida real:

Procesamiento de texto multilingüe (por ejemplo, emojis en la entrada del usuario).


10. ¿Cómo se define un alias de tipo personalizado para int?

Por qué:

Los alias de tipo mejoran la claridad del código y evitan el uso indebido.

Qué:

  • Utilizar type NewName ExistingType.

Cómo:


          type UserID int
          var id UserID =1001
        

Caso de uso en la vida real:

Distinguir los identificadores de bases de datos de los números enteros regulares.


11. ¿Cuál es el valor cero de un slice?

Por qué:

Conocer el estado predeterminado de un slice evita errores de tiempo de ejecución cuando se manipulan datos no inicializados.

Qué:

  • El valor cero de un slice es nil. Un slice nil tiene una longitud y una capacidad de 0.

Cómo:


          var s []int // s == nil
          s = make([]int, 3) // inicializar con longitud 3
        

Caso de uso en la vida real:

Comprobar si un slice es nulo antes de agregar datos en las respuestas de la API para evitar un comportamiento inesperado.


12. ¿Cómo se declara una variable sin inicializarla?

Por qué:

Las variables deben declararse antes de su uso, pero la inicialización puede posponerse.

Qué:

  • Use "var" sin asignación. La variable obtiene su valor cero.

Cómo:


          var count int // count = 0
          var name string // name = ""
        

Caso de uso en la vida real:

Declaración de variables de configuración que se completan posteriormente mediante la entrada del usuario.


13. ¿Por qué no puedes usar ++x en Go (solo x++)?

Por qué:

Go elimina la ambigüedad al restringir el incremento/decremento a la notación sufija.

Qué:

  • x++ es válido (postfijo).
  • ++x no es válido (prefijo).

Cómo:


          x := 5
          x++ // valido (x = 6)
          // ++x  // error de compilación
        

Caso de uso en la vida real:

Evitar efectos secundarios en expresiones como y = x++ + 5, que no están permitidos en Go.


14. Escribir código para intercambiar dos variables sin una variable temporal.

Por qué:

Esto evita la sobrecarga de memoria para intercambios simples.

Qué:

  • Utilizar aritmética o asignación de tuplas.

Cómo:


          a, b := 10, 20
          a, b = b, a  // intercambio de tuplas (a=20, b=10)
        

Caso de uso en la vida real:

Intercambio de valores en algoritmos de ordenamiento (por ejemplo, ordenamiento de burbuja).


15. ¿Cómo maneja Go la división de dos números enteros?

Por qué:

Go sigue las reglas de división de números enteros, descartando el resto.

Qué:

  • 3/2 equivale a 1 (no 1.5).
  • Utilizar tipos flotantes para conservar decimales.

Cómo:


          fmt.Println(3/2) // 1
          fmt.Println(3.0/2.0) // 1.5
        

Caso de uso en la vida real:

Cálculo de puntuaciones medias donde la precisión decimal importa.


16. ¿Cuál es la diferencia entre string y []byte?

Por qué:

Los string son inmutables; []byte es mutable. Elegir el tipo correcto afecta el rendimiento y la seguridad.

Qué:

  • string: secuencia UTF-8 inmutable.
  • []byte: bytes sin procesar mutables.

Cómo:


          s := "hello"
          b := []byte(s) // convertir a bytes mutables
          b[0]='H'
          s = string(b) // "Hello"
        

Caso de uso en la vida real:

Modificación de datos binarios (por ejemplo, procesamiento de imágenes).


17. ¿Cómo se convierte un valor bool en un string?

Por qué:

La conversión de valores booleanos en string es útil para el registro o la visualización de la interfaz de usuario.

Qué:

  • Utilice fmt.Sprintf o un condicional.

Cómo:


          b :=true
          s1 := fmt.Sprintf("%t", b) // "true"
          s2 := "false"; if b { s2 ="true" }
        

Caso de uso en la vida real:

Mostrar “Sí/No” en una interfaz de usuario basada en un indicador booleano.


18. ¿Cuál es la salida de fmt.Println(1 << 10)?

Por qué:

Las operaciones bit a bit son comunes en la programación de bajo nivel.

Qué:

  • 1 << 10 desplaza 1 a la izquierda por 10 bits, lo que da como resultado 1024 (2^10).

Cómo:


          fmt.Println(1 << 10) // 1024
        

Caso de uso en la vida real:

Definición de tamaños de memoria (por ejemplo, 1 KiB = 1024 bytes).


19. ¿Cómo se declara una variable global en un paquete?

Por qué:

Las variables globales persisten durante las llamadas de función, pero pueden introducir dependencias ocultas.

Qué:

  • Se declarar a nivel de paquete usando var.

Cómo:


          package main

          var counter int // variable global

          func increment() {
            counter++
          }
        

Caso de uso en la vida real:

Seguimiento del estado de la aplicación, como los contadores de solicitudes en un servidor web.


20. ¿Cual es el valor cero de un tipo función?

Por qué:

Los tipos funciones en Go pueden ser nulos, lo que resulta útil para los patrones de devolución de llamada.

Qué:

  • El valor cero es nil.

Cómo:


          var f func(int) int // f == nil

          if f != nil {
            f(5) // safeguard
          }
        

Caso de uso en la vida real:

Funciones de devolución de llamada opcionales en librerías (por ejemplo, logging hooks)


21. ¿Cómo se escribe un bucle for sin ninguna condición?

Por qué:

Los bucles infinitos son útiles para procesos de larga ejecución (por ejemplo, servidores).

Qué:

  • Usar for {} sin condición. Salir con "break" o "return".

Cómo:


          for {
            // correr siempre
            if condition {
              break
            }
          }
        

Caso de uso en la vida real:

Ejecutar un servidor TCP que escucha conexiones hasta que se detiene manualmente.


22. ¿Cuál es la diferencia entre const y var?

Por qué:

Las constantes refuerzan la inmutabilidad, mejorando la seguridad y el rendimiento.

Qué:

  • const: Valor fijo (debe poder determinarse en tiempo de compilación).
  • var: Mutable, inicializado en tiempo de ejecución.

Cómo:


          const PI = 3.14
          var radius = 5.0
        

Caso de uso en la vida real:

Usando const para constantes matemáticas como π o flags de configuración.


23. ¿Cómo se declara una variable de tipo complex128?

Por qué:

Los números complejos son esenciales para los cálculos científicos y de ingeniería.

Qué:

  • Utilizar la función complex() o la sintaxis literal.

Cómo:


          c1 := complex(3, 4) // 3 + 4i
          c2 := 5 + 7i // literal corta
        

Caso de uso en la vida real:

Procesamiento de señales (por ejemplo, transformadas de Fourier).


24. ¿Cuál es la salida de len("日本語")?

Por qué:

Los string en Go están codificadas en UTF-8, por lo que len() cuenta bytes, no caracteres.

Qué:

  • “日本語” tiene 3 caracteres Unicode pero 9 bytes.

Cómo:


          fmt.Println(len("日本語")) // salida: 9
        

Caso de uso en la vida real:

Validar la longitud de entrada en campos de formulario multilingües.


25. ¿Cómo se escapa comilla doble en string sin formato?

Por qué:

Los strings sin formato (comillas invertidas) ignoran los caracteres de escape, por lo que el escape requiere una solución alternativa.

Qué:

  • Raw strings no pueden escapar las comillas. Utilizar una cadena normal (") en su lugar.

Cómo:


          s :=`I said, "Hello"`// válido (no es necesario escapar)
        

Caso de uso en la vida real:

Para escribir patrones de expresiones regulares o cadenas JSON sin errores de escape.


26. Explica la diferencia entre runa y byte.

Por qué:

runa maneja Unicode, mientras que byte es para datos sin procesar.

Qué:

  • byte: alias de uint8 (1 byte).
  • runa: alias para int32(4 bytes, representa un punto de código Unicode).

Cómo:


          b := byte('A') // 65
          r := rune('世') // 19990
        

Caso de uso en la vida real:

Procesamiento de emojis o texto no latino (por ejemplo, caracteres chinos).


27. ¿Cómo se formatea un string con argumentos nombrados?

Por qué:

Los argumentos nombrados mejoran la legibilidad en plantillas complejas.

Qué:

  • Utilizar fmt.Sprintf con %[n]v para referenciar argumentos por posición.

Cómo:


          name := "Jesus"
          age := 33
          s := fmt.Sprintf("%[1]s is %[2]d years old. %[1]s likes Go.", name, age)
          // salida: "Jesus is 30 years old. Jesus likes Go."
        

Caso de uso en la vida real:

Generar mensajes de error fáciles de usar con variables repetidas.


28. ¿Cuál es la diferencia entre %v y %+v en fmt.Printf?

Por qué:

%+v agrega nombres de campos al imprimir estructuras, lo que ayuda a la depuración.

Qué:

  • %v: Formato predeterminado.
  • %+v: Incluye nombres de campos de estructura.

Cómo:


          type User struct{Name string; Age int}
          u := User{"Bob", 25}
          fmt.Printf("%v\n", u) // {Bob 25}
          fmt.Printf("%+v\n", u) // {Name:Bob Age:25}
        

Caso de uso en la vida real:

Registro de objetos de solicitud/respuesta de API con contexto de campo.


29. ¿Cómo se imprime la dirección de memoria de una variable?

Por qué:

Las direcciones de memoria ayudan a depurar el comportamiento del puntero u optimizar las estructuras de datos.

Qué:

  • Utilizar & con el especificador de formato %p.

Cómo:


          x :=42
          fmt.Printf("%p", &x) // 0xc0000120a0
        

Caso de uso en la vida real:

Seguimiento de alias de puntero en código concurrente.


30. ¿Por qué Go usa nil en lugar de null?

Por qué:

nil es idiomático en Go y se aplica a punteros, slices, maps, etc.

Qué:

  • nil representa un valor cero para los tipos de referencia.
  • null no es una palabra clave en Go.

Cómo:


          var s []int
          if s == nil {
            fmt.Println("Slice is nil")
          }
        

Caso de uso en la vida real:

Comprobar si un map/slice se ha inicializado antes de su uso.


Flujo de control (20 preguntas)

1. ¿En qué se diferencia la instrucción switch de Go de la de C?

Por qué:

Go simplifica el switch para reducir errores (por ejemplo, omisiones accidentales).

Qué:

  • No es necesario break (los casos no se descartan por defecto).
  • Admite expresiones, tipos y transición (fallthrough).

Cómo:


          fruit := "apple"
          switch fruit {
          case "banana":
            fmt.Println("Yellow")
          case "apple":
            fmt.Println("Red") // Imprime "Red" y sale.
          }
        

Caso de uso en la vida real:

Manejo limpio de códigos de estado HTTP sin if-else anidados.


2. Escribir un bucle for que itere sobre una cadena de caracteres Unicode.

Por qué:

Los string están codificados en UTF-8; la iteración por runa garantiza exactitud.

Qué:

  • User "for range" para obtener valores de runas, no índices de bytes.

Cómo:


          s :="日本語"
          for index, char := range s {
            fmt.Printf("%d: %c\n", index, char)
          }
          // salida:
          // 0: 日
          // 3: 本
          // 6: 語
        

Caso de uso en la vida real:

Validar emojis o caracteres multibyte en la entrada del usuario.


3. ¿Cómo se simula un bucle while en Go?

Por qué:

Go utiliza for en todos los bucles, mejorando la consistencia.

Qué:

  • Usar for con una sola condición (sin init/post).

Cómo:


          count := 0
          for count < 5 {
            fmt.Println(count)
            count++
          }
        

Caso de uso en la vida real:

Sondear una API hasta que se cumpla una condición (por ejemplo, la finalización de una tarea).


4. ¿Cuál es la salida de "defer" en un bucle? Explicar con código.

Por qué:

La postergación (defer) en bucle puede generar un comportamiento no deseado si no se maneja con cuidado.

Qué:

  • Defer retrasa la ejecución hasta que la función circundante retorna. En los bucles, las llamadas diferidas se apilan y se ejecutan en orden LIFO.

Cómo:


          func main() {
            for i := 0; i < 3; i++ {
              defer fmt.Println(i) // difiere la pila: 2, 1, 0
            }
          }
          // salida:
          // 2
          // 1
          // 0
        

Caso de uso en la vida real:

Aplazar accidentalmente la limpieza de recursos en un bucle puede demorarla hasta después de todas las iteraciones.


5. ¿Cómo funciona goto en Go? Dar un caso de uso válido.

Por qué:

goto se utiliza rara vez, pero puede simplificar el manejo de errores en código anidado.

Qué:

  • Salta a una declaración etiquetada dentro de la misma función.

Cómo:


          func process() error {
            if err := step1(); err != nil {
              goto cleanup
            }
            // ...
            cleanup:
              fmt.Println("Limpieza de recursos")
              return nil
          }
        

Caso de uso en la vida real:

Centralizar la lógica de limpieza en funciones con múltiples rutas de error.


6. ¿Cómo se omite una iteración en un bucle usando continue?

Por qué:

continue omite iteraciones específicas sin salir del bucle.

Qué:

  • Pasar a la siguiente iteración si se cumple una condición.

Cómo:


          for i := 0; i < 10; i++ {
            if i%2 == 0 {
              continue // saltar números pares
            }
            fmt.Println(i)
          }
        

Caso de uso en la vida real:

Filtrar entradas de datos no válidas durante el procesamiento por lotes.


7. Escribir el código para salir de un bucle anidado usando break con etiquetas.

Por qué:

Las etiquetas permiten salir de los bucles externos, evitando las variables de flag

Qué:

  • Etiquetar el bucle exterior y utilizar la etiqueta de interrupción.

Cómo:


          outer:
          for i := 0; i < 3; i++ {
            for j := 0; j < 3; j++ {
              if i*j == 4 {
                break outer // sale de ambos bucles
              }
            }
          }
        

Caso de uso en la vida real:

Buscando en un grid 2D y deteniéndose cuando se encuentra un objetivo.


8. ¿Cómo funciona fallthrough en una sentencia switch?

Por qué:

fallthrough permite un comportamiento de fallthrough similar al del case en C si es explícitamente necesario.

Qué:

  • Fuerza la ejecución para continuar con el siguiente caso.

Cómo:


          n := 2
          switch n {
          case 2:
            fmt.Println("Two")
            fallthrough
          case 3:
            fmt.Println("Three")
          }
          // salida:
          // Two
          // Three
        

Caso de uso en la vida real:

Rara vez se utiliza, pero puede agrupar la lógica compartida para múltiples casos.


9. ¿Cómo se escribe un bucle infinito utilizando for?

Por qué:

Los bucles infinitos se utilizan en servidores, programadores o procesos de ejecución prolongada.

Qué:

  • Usar for sin ninguna condición.

Cómo:


          for{
            // Correr siempre
            if condition {
              break
            }
          }
        

Caso de uso en la vida real:

Un trabajo en segundo plano que supervisa una cola de mensajes.


10. ¿Cuál es la diferencia entre if x {} y if x == true {}?

Por qué:

Go fomenta comprobaciones booleanas concisas.

Qué:

  • if x {} es idiomático cuando x es un booleano.
  • if x == true {} es redundante.

Cómo:


          x := true
          if x {/* preferido */}
          if x == true {/* evitar */}
        

Caso de uso en la vida real:

Comprobación de características o condiciones sin verbosidad innecesaria.


11. ¿Cómo se manejan múltiples condiciones en una declaración if?

Por qué:

La combinación de condiciones reduce los bloques if anidados y mejora la legibilidad.

Qué:

  • Utilizar operadores lógicos (&&, ||, !).

Cómo:


          age := 25
          ifage >= 18 && age <= 60 {
            fmt.Println("Eligible to work")
          }
        

Caso de uso en la vida real:

Validar entrada de formulario (por ejemplo, verificar la edad y el formato del correo electrónico).


12. Escribir código para demostrar la evaluación de cortocircuito en Go.

Por qué:

El cortocircuito optimiza las operaciones lógicas al omitir evaluaciones innecesarias.

Qué:

  • && detiene si la primera condición es falsa.
  • || detiene si la primera condición es verdadera.

Cómo:


          func isEven(n int) bool {
            fmt.Println("Called")
            return n%2 == 0
          }

          if false && isEven(4) { // isEven() nunca se llama
            // ...
          }
        

Caso de uso en la vida real:

Evitar cálculos costosos cuando falla una condición previa (por ejemplo, verificar permisos antes de acceder a los datos).


13. ¿Cómo se utiliza defer dentro de un bloque condicional?

Por qué:

defer se aplica a la función circundante, no al bloque condicional.

Qué:

  • La llamada diferida se ejecuta cuando sale la función, no el bloque.

Cómo:


          func process(flag bool) {
            if flag {
              defer fmt.Println("Cleanup A")
            }
            defer fmt.Println("Cleanup B")
          }

          process(true)
          // salida: Cleanup B → Cleanup A
        

Caso de uso en la vida real:

Limpieza de recursos condicional (por ejemplo, cerrar archivos solo si se abren).


14. ¿Qué sucede si llamas a panic() sin recover()?

Por qué:

Los panic no recuperados hacen que el programa se bloquee, lo cual es fundamental para el manejo rápido de errores.

Qué:

  • El programa finaliza con un seguimiento de la pila.

Cómo:


          func main(){
            panic("Something went wrong")
            fmt.Println("This line never runs")
          }
        

Caso de uso en la vida real:

Fallos catastróficos (por ejemplo, pérdida irrecuperable de la conexión a la base de datos).


15. ¿Cómo se anidan las sentencias switch?

Por qué:

Los switch anidados manejan árboles de decisiones de múltiples niveles (por ejemplo, analizan datos anidados).

Qué:

  • Un switch dentro de otro switch o case.

Cómo:


          value := 42
          switch {
            case value > 0:
            switch value {
              case 42:
              fmt.Println("The answer")
            }
          }
        

Caso de uso en la vida real:

Analizar archivos de configuración con reglas jerárquicas.


16. ¿Cómo se utiliza range para iterar sobre un mapa?

Por qué:

Iterar mapas con range es seguro pero sin orden por diseño.

Qué:

  • Cada iteración devuelve un par clave-valor aleatorio.

Cómo:


          m := map[string]int{"a":1, "b":2}
          for key, value := range m {
            fmt.Printf("%s: %d\n", key, value)
          }
        

Caso de uso en la vida real:

Agregación de métricas almacenadas en un mapa (por ejemplo, contar endpoints de API).


17. Escribir código para iterar sobre un slice hacia atrás.

Por qué:

La iteración hacia atrás es útil para el procesamiento inverso (por ejemplo, deshacer operaciones).

Qué:

  • Bucle desde len(slice)-1 hasta 0

Cómo:


          s := []int{1, 2, 3}
          for i := len(s)-1; i >= 0; i-- {
            fmt.Println(s[i]) // 3 → 2 → 1
          }
        

Caso de uso en la vida real:

Invertir el orden de entradas del registro para su visualización.


18. ¿Cómo se maneja panic en una función diferida?

Por qué:

Las funciones diferidas pueden interceptar y recuperarse de los panics.

Qué:

  • Utilizar recover() dentro de una función diferida.

Cómo:


          func safeCall() {
            defer func() {
              if r :=recover(); r !=nil {
                fmt.Println("Recovered:",r)
              }
            }()
            panic("Intentional panic")
          }
        

Caso de uso en la vida real:

Apagar controladamente un servidor después de un panic en lugar de bloquearlo.


19. ¿Cómo funciona select con casos de canales múltiples?

Por qué:

select permite comunicación sin bloqueos a través de múltiples canales.

Qué:

  • Ejecuta la primera operación del un canal listo (enviar/recibir).

Cómo:


          ch1 := make(chan string)
          ch2 := make(chan string)

          go func() { ch1 <- "hello" }()
          go func() { ch2 <- "world" }()

          select{
            case msg := <-ch1:
              fmt.Println(msg)
            case msg := <-ch2:
              fmt.Println(msg)
          }
        

Caso de uso en la vida real:

Implementar tiempos de espera o priorizar operaciones de canal.


20. ¿Cuál es el resultado de defer fmt.Print("A"); fmt.Print("B")?

Por qué:

defer retrasa la ejecución hasta que la función sale.

Qué:

  • Las llamadas diferidas se ejecutan en orden LIFO (último en entrar, primero en salir).

Cómo:


          func main() {
            defer fmt.Print("A")
            fmt.Print("B")
          }
          // salida: B A
        

Caso de uso en la vida real:

Registrar acciones de limpieza una vez completada la lógica principal.


Paquetes y módulos (30 preguntas)

1. ¿Cómo crear e importar un paquete local?

Por qué:

Los paquetes locales modularizan el código, mejorando la capacidad de mantenimiento y la reutilización.

Qué:

  • Crear un directorio con archivos Go (por ejemplo, utils/math.go).
  • Declarar el nombre del paquete (por ejemplo, paquete utils).
  • Importar utilizando la ruta del módulo definida en go.mod.

Cómo:


          // utils/math.go
          package utils

          func Add(a, b int) int {
            return a + b
          }

          // main.go
          package main

          import(
          "fmt"
          "your-module-path/utils" // reemplazar con nombre de módulo en go.mod
          )

          func main() {
            sum := utils.Add(3, 5)
            fmt.Println(sum) // 8
          }
        

Caso de uso en la vida real:

Dividir una base de código monolítica en componentes reutilizables (por ejemplo, autenticación, base de datos, registrador).


2. ¿Cuál es el propósito de go.mod? ¿Cómo reemplaza a GOPATH?

Por qué:

go.mod permite la gestión de dependencias teniendo en cuenta los módulos, desacoplando los proyectos de la estructura rígida de GOPATH.

Qué:

  • go.mod: define la ruta del módulo, la versión de Go y las dependencias.
  • GOPATH: Espacio de trabajo heredado para código Go (obsoleto en favor de los módulos).

Cómo:


          // go.mod
          module github.com/yourusername/project

          go1.21

          require (
          github.com/gorilla/mux v1.8.0
          )
        

Caso de uso en la vida real:

Trabajando en múltiples proyectos con versiones de dependencia en conflicto.


3. Explica la diferencia entre go install y go build.

Por qué:

Comprender las herramientas de compilación garantiza flujos de trabajo de desarrollo eficientes.

Qué:

  • go build: Compila el código en un archivo ejecutable en el directorio actual.
  • go install: Compila e instala el ejecutable en $GOPATH/bin.

Cómo:


          go build main.go       # crea ./main
          go install main.go     # se instala en $GOPATH/bin/main
        

Caso de uso en la vida real:

Instalación global de herramientas de línea de comandos (por ejemplo, go install github.com/your-tool@latest).


4. ¿Cómo se manejan las dependencias cíclicas en Go?

Por qué:

Las dependencias cíclicas provocan errores de compilación e indican un diseño deficiente.

Qué:

  • Refactoriza el código para romper ciclos (por ejemplo, traslada la lógica compartida a un tercer paquete).

Cómo:


          // Mal: pkg A importa pkg B, que a su vez importa pkg A
          // Solución: Crear pkg Common con tipos/funciones compartidas.
        

Caso de uso en la vida real:

Separar los modelos de dominio (usuario, producto) de las capas de servicio.


5. ¿Qué sucede si dos paquetes tienen el mismo nombre?

Por qué:

Las colisiones de nombres de paquetes pueden causar ambigüedad en las importaciones.

Qué:

  • Utilizar alias para resolver conflictos.

Cómo:


          import(
            m "your-module/math"
            othermath "github.com/other/math"
          )
        

Caso de uso en la vida real:

Utilizar bibliotecas de terceros con nombres incompatibles (por ejemplo, log vs. custom log).


6. ¿Cómo se gestiona el versionado de un módulo Go?

Por qué:

El versionado semántico garantiza la compatibilidad y la reproducibilidad.

Qué:

  • Etiquetar los commits con vMAJOR.MINOR.PATCH (por ejemplo, git tag v1.0.0).

Cómo:


          git tag v1.2.3
          git push origin v1.2.3
        

Caso de uso en la vida real:

Publicación de versiones estables de una libreria para uso público.


7. ¿Cuál es el propósito de go.sum?

Por qué:

go.sum garantiza la integridad de las dependencias almacenando sumas de verificación criptográficas.

Qué:

  • Registra los hashes válidos esperados para las dependencias del módulo.
  • Evita manipulaciones o cambios accidentales.

Cómo:


          // go.sum
          github.com/gorilla/mux v1.8.0h1:...
          github.com/gorilla/mux v1.8.0/go.mod h1:...
        

Caso de uso en la vida real:

Verificación de dependencias en pipelines de CI/CD para auditorías de seguridad.


8. ¿Cómo se actualiza una dependencia a una versión específica?

Por qué:

Fijar versiones evita cambios inesperados que puedan causar problemas.

Qué:

  • Usar go get module@version.

Cómo:


          go get github.com/gorilla/mux@v1.8.0
        

Caso de uso en la vida real:

Reducir la versión de una dependencia tras una regresión en una versión más reciente.


9. ¿Qué es una directiva replace en go.mod?

Por qué:

La opción `replace` permite el desarrollo local o la modificación temporal de dependencias.

Qué:

  • Redirige la ruta de un módulo a un directorio local o a otra versión.

Cómo:


          // go.mod
          replace github.com/your/dep => ../local-dep
        

Caso de uso en la vida real:

Probar una bifurcación local de una dependencia antes de contribuir al proyecto original.


10. ¿Cómo se gestionan las dependencias de proveedores usando go mod vendor?

Por qué:

La gestión de proveedores garantiza compilaciones sin conexión y entornos reproducibles.

Qué:

  • Copia las dependencias en un directorio `vendor`.

Cómo:


          go mod vendor
        

Caso de uso en la vida real:

Proyectos de construcción en entornos aislados sin acceso a la red externa.


11. ¿Cómo se utilizan las etiquetas de compilación para compilar código de forma condicional?

Por qué:

Las etiquetas de compilación permiten ejecutar código específico de la plataforma o restringido a funciones sin saturar los archivos.

Qué:

  • Agregar //go:build <-tag-> en la parte superior del archivo.
  • Utilizar -tags durante la compilación para incluir/excluir código.

Cómo:


          //go:build debug

          package main

          func debugLog() {
            fmt.Println("Debug mode enabled")
          }

          go build -tags debug
        

Caso de uso en la vida real:

Incluir registros de depuración o funciones experimentales solo en versiones de desarrollo.


12. ¿Cuál es el propósito de //go:generate?

Por qué:

Automatización de la generación de código (por ejemplo, mediante mocks o protobufs) para reducir el código repetitivo.

Qué:

  • Una directiva para ejecutar generadores de código mediante go generate.

Cómo:


          //go:generate mockgen -source=user.go -destination=mock_user.go

          go generate ./...
        

Caso de uso en la vida real:

Generación de código cliente/servidor gRPC a partir de archivos .proto


13. ¿Cómo se estructura un proyecto Go con múltiples paquetes?

Por qué:

Una estructura clara mejora la facilidad de mantenimiento y la colaboración.

Qué:

  • Ejemplo de diseño como referencia.

Cómo:


          project/
          ├── cmd/          // Puntos de entrada (paquetes principales)
          ├── internal/     // Código privado (no importable)
          ├── pkg/          // Código público reutilizable
          └── go.mod
        

Caso de uso en la vida real:

Separación de los puntos de entrada de la interfaz de línea de comandos (cmd/cli), la lógica de negocio (internal/services) y las utilidades compartidas (pkg/utils).


14. ¿Cómo se inicializa un nuevo módulo con go mod init?

Por qué:

Los módulos definen las dependencias y el control de versiones para lograr compilaciones reproducibles.

Qué:

  • Crea un archivo go.mod con la ruta del módulo.

Cómo:


          go mod init github.com/yourusername/project
        

Caso de uso en la vida real:

Iniciar un nuevo proyecto o convertir un proyecto GOPATH heredado en módulos.


15. ¿Qué es modo GOPATH?

Por qué:

GOPATH era el espacio de trabajo previo a los módulos para el código Go, ahora prácticamente obsoleto.

Qué:

  • Todo el código residía en $GOPATH/src
  • Sin control de versiones ni aislamiento de dependencias

Cómo:


          export GOPATH=$HOME/go
        

Caso de uso en la vida real:

Mantenimiento de proyectos heredados que no se han migrado a módulos Go.


16. ¿Cómo se importa un paquete con un alias (por ejemplo, import m "math")?

Por qué:

Los alias resuelven conflictos de nombres o acortan nombres de paquetes largos.

Qué:

  • Utilizar la sintaxis [alias] "[ruta-del-paquete]".

Cómo:


          import(
            m "math"
            "github.com/other/math"
          )

          func main() {
            fmt.Println(m.Sqrt(4)) // usando alias "m"
          }
        

Caso de uso en la vida real:

Evitar conflictos entre un paquete de utilidades local y una biblioteca de utilidades de terceros.


17. ¿Cuál es la convención de paquetes internos en Go?

Por qué:

Impide que proyectos externos importen código confidencial o interno.

Qué:

  • El código en internal/ solo puede ser importado por paquetes dentro del mismo módulo.

Cómo:


          project/
          ├── internal/
          │   └── auth/  // No puede ser importado por módulos externos
          └── go.mod
        

Caso de uso en la vida real:

Ocultar la lógica de conexión a la base de datos o algoritmos propietarios.


18. ¿Cómo se ejecutan las pruebas para todos los paquetes de un módulo?

Por qué:

Garantiza que todos los componentes cumplan con estándares de calidad antes de su implementación.

Qué:

  • Usar go test ./... para probar recursivamente todos los paquetes.

Cómo:


          go test -v ./...
        

Caso de uso en la vida real:

Ejecución de pruebas unitarias, de integración y de rendimiento en pipelines de CI/CD.


19. ¿Cómo se realiza la compilación cruzada de código Go para Windows desde Linux?

Por qué:

La compilación cruzada simplifica la creación de binarios para múltiples plataformas.

Qué:

  • Usar las variables de entorno GOOS y GOARCH

Cómo:


          GOOS=windows GOARCH=amd64 go build -o app.exe
        

Caso de uso en la vida real:

Creación de una herramienta de línea de comandos de Windows en un servidor de integración continua Linux.


20. ¿Cuál es el propósito de go get -u?

Por qué:

La actualización de las dependencias garantiza el acceso a las últimas funciones y parches de seguridad.

Qué:

  • -u actualiza las dependencias a sus últimas versiones menores/de parche.

Cómo:


          go get -u github.com/gorilla/mux
        

Caso de uso en la vida real:

Mantener actualizado un framework web con las nuevas funciones de enrutamiento.


21. ¿Cómo se fija una dependencia a un commit específico?

Por qué:

Fijar un cambio a un commit garantiza la reproducibilidad, especialmente si la etiqueta de versión no está disponible o es inestable.

Qué:

  • Usar go get con el hash del commit.

Cómo:


          go get github.com/gorilla/mux@ef69d98... # Colocar hash de commit completo
        

Caso de uso en la vida real:

Probando una corrección de errores de una pull request que aún no se ha publicado como una versión etiquetada.


22. ¿Cómo se resuelve un conflicto de dependencias?

Por qué:

Los conflictos surgen cuando dos paquetes requieren versiones incompatibles de la misma dependencia.

Qué:

  • Usar go mod tidy para resolver automáticamente los conflictos.
  • Actualizar manualmente las dependencias a versiones compatibles.

Cómo:


          go mod tidy
        

Caso de uso en la vida real:

Solución de problemas de incompatibilidad de versiones en proyectos grandes con muchas librería de terceros.


23. ¿Cómo se usa go mod tidy?

Por qué:

Limpia las dependencias no utilizadas y actualiza go.mod/go.sum para reflejar las importaciones reales.

Qué:

  • Agrega las dependencias faltantes.
  • Elimina las dependencias no utilizadas.

Cómo:


          go mod tidy
        

Caso de uso en la vida real:

Eliminar dependencias tras refactorizar el código para suprimir importaciones obsoletas.


24. ¿Cómo se excluye una versión específica de una dependencia?

Por qué:

Excluir las versiones defectuosas o inseguras evita su uso accidental.

Qué:

  • Agregar una directiva de exclusión en go.mod.

Cómo:


          // go.mod
          exclude github.com/old/dep v1.2.3
        

Caso de uso en la vida real:

Bloqueo de una versión de dependencia con una vulnerabilidad de seguridad conocida.


25. ¿Qué es un archivo go.work?

Por qué:

Permite el modo de espacio de trabajo para desarrollar localmente múltiples módulos interdependientes.

Qué:

  • Define un espacio de trabajo de módulos.
  • Sobrescribe dependencias con versiones locales.

Cómo:


          go work init
          go work use ./module-a ./module-b
        

Caso de uso en la vida real:

Desarrollar simultáneamente un microservicio y su módulo de biblioteca compartida.


26. ¿Cómo se utilizan etiquetas durante compilación?

Por qué:

Las etiquetas incluyen de forma condicional código para entornos específicos (por ejemplo, desarrollo, producción).

Qué:

  • Compilar con -tags seguido de una lista separada por comas.

Cómo:


          go build -tags "debug,json"
        

Caso de uso en la vida real:

Habilitación de funciones experimentales en una versión beta.


27. ¿Cómo se compila un binario de Go con símbolos de depuración?

Por qué:

Los símbolos de depuración ayudan en la elaboración de perfiles o en el análisis de fallos.

Qué:

  • Utilizar -ldflags para deshabilitar la eliminación de símbolos.

Cómo:


          go build -ldflags "-w=false"
        

Caso de uso en la vida real:

Depuración de fugas de memoria en un binario de producción con pprof.


28. ¿Cuál es el propósito de go env?

Por qué:

Inspecciona y modifica las variables de entorno de Go (por ejemplo, GOPATH, GOOS).

Qué:

  • Mostrar la configuración actual: go env
  • Sobreescribir las variables: go env -w CLAVE=VALOR

Cómo:


          go env GOPATH     # ver GOPATH
          go env -w GO111MODULE=on # si modo go modules activo
        

Caso de uso en la vida real:

Configurar un proyecto para que utilice un proxy específico para la descarga de dependencias.


29. ¿Cómo se desactiva temporalmente la compatibilidad con módulos de Go?

Por qué:

Los proyectos heredados podrían depender de GOPATH y requerir que se desactive la compatibilidad con módulos.

Qué:

  • Establecer GO111MODULE=off en el entorno de Go.

Cómo:


          GO111MODULE=off go build
        

Caso de uso en la vida real:

Probando crear un proyecto Go con modo previo módulos sin refactorizar su estructura.


30. ¿Cómo se listan todas las dependencias de un módulo?

Por qué:

La auditoría de dependencias ayuda a realizar un seguimiento de las licencias o los problemas de seguridad.

Qué:

  • Usar `go list` para mostrar el gráfico de dependencias del módulo.

Cómo:


          go list -m all
        

Caso de uso en la vida real:

Generación de una lista de materiales de software (SBOM) para el cumplimiento normativo.


Conceptos básicos de memoria (20 preguntas)

1. ¿Cómo asigna Go memoria para los slices?

Por qué:

Optimice el uso de la memoria comprendiendo el funcionamiento interno de los segmentos de memoria.

Qué:

  • Una slice es una estructura con un puntero a un array, una longitud y una capacidad. Cuando se añaden elementos que superan la capacidad, Go reasigna un array más grande (normalmente duplicando su tamaño).

Cómo:


          s := make([]int, 0, 5) // capacidad inicial 5
          s = append(s, 1, 2) // no hay reasignación
          s = append(s, 3, 4, 5, 6) // la capacidad se duplica a 10
        

Caso de uso en la vida real:

Gestionar de forma eficiente los datos dinámicos en las API o en los flujos de datos.


2. ¿Cuál es la diferencia entre new() y make()?

Por qué:

Evitar errores en tiempo de ejecución inicializando correctamente los tipos.

Qué:

  • new(T): Asigna memoria cero para el tipo T y devuelve un *T.
  • make(T): Inicializa slices, maps o channels (no punteros).

Cómo:


          p := new(int) // *int que apunta a 0
          m := make(map[string]int) // mapa inicializado
        

Caso de uso en la vida real:

'make' para canales en trabajadores concurrentes; 'new' para estructuras personalizadas.