Hoja de Trucos de Golang

Aprende Golang con Laboratorios Prácticos

Aprende programación Go a través de laboratorios prácticos y escenarios del mundo real. LabEx proporciona cursos completos de Go que cubren sintaxis esencial, patrones de concurrencia, manejo de errores y técnicas avanzadas. Domina las características únicas de Go como goroutines, canales e interfaces para construir aplicaciones concurrentes y eficientes.

Instalación y Configuración

Instalar Go: Descargar y Extraer

Descarga e instala Go desde el sitio web oficial.

# Descargar desde https://golang.org/dl/
# Linux/macOS - extraer en /usr/local
sudo tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
# Añadir a PATH en ~/.bashrc o ~/.zshrc
export PATH=$PATH:/usr/local/go/bin
# Verificar instalación
go version

Gestor de Paquetes: Usando Homebrew (macOS)

Instala Go usando Homebrew en macOS.

# Instalar Go con Homebrew
brew install go
# Verificar instalación
go version
go env GOPATH

Instalación en Windows

Instala Go en sistemas Windows.

# Descargar el instalador .msi desde https://golang.org/dl/
# Ejecutar el instalador y seguir las instrucciones
# Verificar en el Símbolo del sistema (Command Prompt)
go version
echo %GOPATH%

Configuración del Espacio de Trabajo: go mod init

Inicializa un nuevo módulo y espacio de trabajo de Go.

# Crear nuevo directorio e inicializar módulo
mkdir my-go-project
cd my-go-project
go mod init my-go-project
# Crear main.go
echo 'package main
import "fmt"
func main() {
    fmt.Println("Hello, World!")
}' > main.go
# Ejecutar el programa
go run main.go

Variables de Entorno

Variables de entorno importantes de Go.

# Ver todas las variables de entorno de Go
go env
# Variables clave
go env GOROOT    # Directorio de instalación de Go
go env GOPATH    # Directorio del espacio de trabajo
go env GOOS      # Sistema operativo
go env GOARCH    # Arquitectura

Configuración del IDE: VS Code

Configura VS Code para el desarrollo en Go.

# Instalar la extensión Go en VS Code
# Ctrl+Shift+P -> Go: Install/Update Tools
# Características clave habilitadas:
# - Resaltado de sintaxis
# - IntelliSense
# - Depuración (Debugging)
# - Integración de pruebas (Testing)

Sintaxis Básica y Tipos

Paquete e Importaciones

Cada archivo Go comienza con una declaración de paquete e importaciones.

package main
import (
    "fmt"
    "strings"
    "time"
)
// Importación única
import "os"

Variables y Constantes

Declara e inicializa variables y constantes.

// Declaraciones de variables
var name string = "Go"
var age int = 15
var isOpen bool
// Declaración corta
name := "Golang"
count := 42
// Constantes
const Pi = 3.14159
const Message = "Hello, Go!"
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Cuál es la diferencia entre var name string = "Go" y name := "Go"?
No hay diferencia
:= es una declaración corta que infiere el tipo, var declara explícitamente el tipo
:= solo se puede usar para constantes
var solo se puede usar dentro de funciones

Tipos de Datos Básicos

Tipos fundamentales disponibles en Go.

// Tipos numéricos
var i int = 42
var f float64 = 3.14
var c complex64 = 1 + 2i
// Tipos de texto
var s string = "Hello"
var r rune = 'A'
// Booleano
var b bool = true

Flujo de Control

Condicionales: if / else / switch

Controla el flujo del programa con sentencias condicionales.

// Sentencias If
if age >= 18 {
    fmt.Println("Adulto")
} else if age >= 13 {
    fmt.Println("Adolescente")
} else {
    fmt.Println("Niño")
}
// Sentencias Switch
switch day {
case "Lunes":
    fmt.Println("Inicio de la semana laboral")
case "Viernes":
    fmt.Println("Viernes")
default:
    fmt.Println("Día normal")
}

Bucles: for / range

Itera usando varias construcciones de bucle.

// Bucle for tradicional
for i := 0; i < 10; i++ {
    fmt.Println(i)
}
// Bucle estilo While
for condition {
    // cuerpo del bucle
}
// Bucle infinito
for {
    // romper cuando sea necesario
}

Iteración con Range

Itera sobre slices, arrays, mapas y cadenas.

// Iterar sobre slice
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
    fmt.Printf("Índice: %d, Valor: %d\n", index, value)
}
// Iterar sobre mapa
colors := map[string]string{"red": "#FF0000", "green": "#00FF00"}
for key, value := range colors {
    fmt.Printf("%s: %s\n", key, value)
}
// Iterar sobre cadena (string)
for i, char := range "Hello" {
    fmt.Printf("%d: %c\n", i, char)
}
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué devuelve range al iterar sobre un slice en Go?
Solo el valor
Tanto el índice como el valor
Solo el índice
La longitud del slice

Sentencias de Control: break / continue

Controla el flujo de ejecución del bucle.

// Salir del bucle
for i := 0; i < 10; i++ {
    if i == 5 {
        break
    }
    fmt.Println(i)
}
// Omitir la iteración actual
for i := 0; i < 5; i++ {
    if i == 2 {
        continue
    }
    fmt.Println(i)
}
Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Cuál es la diferencia entre break y continue en los bucles de Go?
No hay diferencia
break omite la iteración actual, continue sale del bucle
break sale completamente del bucle, continue salta a la siguiente iteración
Ambos salen del bucle

Funciones

Declaración de Función: func

Define funciones con parámetros y valores de retorno.

// Función básica
func greet(name string) {
    fmt.Printf("Hola, %s!\n", name)
}
// Función con valor de retorno
func add(a, b int) int {
    return a + b
}
// Múltiples valores de retorno
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("división por cero")
    }
    return a / b, nil
}

Retornos Nombrados y Funciones Variádicas

Características avanzadas de las funciones y patrones.

// Valores de retorno nombrados
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // retorno desnudo
}
// Función variádica
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}
// Uso
result := sum(1, 2, 3, 4, 5)

Tipos de Función y Clausuras (Closures)

Funciones como ciudadanos de primera clase en Go.

// Función como variable
var multiply func(int, int) int
multiply = func(a, b int) int {
    return a * b
}
// Función anónima
square := func(x int) int {
    return x * x
}
// Clausura (Closure)
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
// Uso
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2

Sentencia Defer

Difiere la ejecución de funciones hasta que la función circundante retorna.

func processFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close() // Se ejecuta cuando la función retorna

    // Procesar contenido del archivo
    // file.Close() se llamará automáticamente
}

Estructuras de Datos

Arrays y Slices

Secuencias fijas y dinámicas de elementos.

// Arrays (tamaño fijo)
var arr [5]int = [5]int{1, 2, 3, 4, 5}
shortArr := [3]string{"a", "b", "c"}
// Slices (dinámicos)
var slice []int
slice = append(slice, 1, 2, 3)
// Crear slice con capacidad
numbers := make([]int, 5, 10) // longitud 5, capacidad 10
// Operaciones de slice
slice2 := slice[1:3]  // [2, 3]
copy(slice2, slice)   // Copiar elementos

Mapas (Maps)

Pares clave-valor para búsquedas eficientes.

// Declaración e inicialización de mapa
var m map[string]int
m = make(map[string]int)
// Declaración corta
ages := map[string]int{
    "Alice": 30,
    "Bob":   25,
    "Carol": 35,
}
// Operaciones de mapa
ages["David"] = 40        // Añadir/actualizar
delete(ages, "Bob")       // Eliminar
age, exists := ages["Alice"] // Comprobar existencia

Estructuras (Structs)

Agrupa datos relacionados con tipos personalizados.

// Definición de struct
type Person struct {
    Name    string
    Age     int
    Email   string
}
// Crear instancias de struct
p1 := Person{
    Name:  "Alice",
    Age:   30,
    Email: "alice@example.com",
}
p2 := Person{"Bob", 25, "bob@example.com"}
// Acceder a campos
fmt.Println(p1.Name)
p1.Age = 31

Punteros (Pointers)

Referencia las direcciones de memoria de las variables.

// Declaración de puntero
var p *int
num := 42
p = &num  // Dirección de num
// Desreferenciación
fmt.Println(*p) // Valor en la dirección (42)
*p = 100        // Cambiar valor a través del puntero
// Punteros con structs
person := &Person{Name: "Alice", Age: 30}
person.Age = 31  // Desreferenciación automática

Métodos e Interfaces

Métodos

Asocia funcionalidad a tipos personalizados.

type Rectangle struct {
    Width, Height float64
}
// Método con receptor (receiver)
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
// Receptor de puntero (puede modificar)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}
// Uso
rect := Rectangle{Width: 10, Height: 5}
fmt.Println(rect.Area()) // 50
rect.Scale(2)            // Modifica rect

Interfaces

Definen contratos que los tipos deben satisfacer.

// Definición de interfaz
type Shape interface {
    Area() float64
    Perimeter() float64
}
// Implementar interfaz para Rectangle
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}
// Rectangle ahora implementa la interfaz Shape
func printShapeInfo(s Shape) {
    fmt.Printf("Área: %.2f, Perímetro: %.2f\n",
               s.Area(), s.Perimeter())
}

Interfaz Vacía y Asertos de Tipo (Type Assertions)

Trabaja con valores de tipos desconocidos.

// La interfaz vacía puede contener cualquier valor
var i interface{}
i = 42
i = "hello"
i = []int{1, 2, 3}
// Aserto de tipo
str, ok := i.(string)
if ok {
    fmt.Printf("Valor de cadena: %s\n", str)
}
// Switch de tipo
switch v := i.(type) {
case int:
    fmt.Printf("Entero: %d\n", v)
case string:
    fmt.Printf("Cadena: %s\n", v)
default:
    fmt.Printf("Tipo desconocido: %T\n", v)
}

Incrustación (Embedding)

Componer tipos incrustando otros tipos.

type Person struct {
    Name string
    Age  int
}
type Employee struct {
    Person    // Struct incrustado
    Company   string
    Salary    float64
}
// Uso
emp := Employee{
    Person:  Person{Name: "Alice", Age: 30},
    Company: "TechCorp",
    Salary:  75000,
}
// Acceder a campos incrustados directamente
fmt.Println(emp.Name) // "Alice"

Manejo de Errores

Manejo Básico de Errores

Usa la interfaz de error incorporada para el manejo de errores.

import "errors"
// Función que retorna un error
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("división por cero")
    }
    return a / b, nil
}
// Verificación de errores
result, err := divide(10, 2)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Resultado: %.2f\n", result)

Errores Personalizados

Crea tipos de error personalizados para condiciones de error específicas.

// Tipo de error personalizado
type ValidationError struct {
    Field   string
    Message string
}
func (e ValidationError) Error() string {
    return fmt.Sprintf("error de validación en %s: %s",
                       e.Field, e.Message)
}
// Función que usa error personalizado
func validateAge(age int) error {
    if age < 0 {
        return ValidationError{
            Field:   "age",
            Message: "debe ser no negativo",
        }
    }
    return nil
}

Envoltura de Errores (Error Wrapping)

Añade contexto a los errores mientras se preserva el error original.

import "fmt"
// Envolver un error con contexto adicional
func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return fmt.Errorf("falló al abrir el archivo %s: %w",
                          filename, err)
    }
    defer file.Close()

    // Procesar archivo...
    return nil
}
// Desenvolver errores
err := processFile("missing.txt")
if err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) {
        fmt.Println("Error de ruta:", pathErr)
    }
}

Panic y Recuperación (Recovery)

Maneja situaciones excepcionales con panic y recover.

// Función que podría causar pánico
func riskyOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recuperado de pánico:", r)
        }
    }()

    // Esto causará un pánico
    panic("¡algo salió mal!")
}
// Uso
riskyOperation() // El programa continúa después del pánico

Concurrencia

Goroutines

Hilos ligeros gestionados por el runtime de Go.

import "time"
// Goroutine simple
func sayHello() {
    fmt.Println("Hola desde goroutine!")
}
func main() {
    // Iniciar goroutine
    go sayHello()

    // Goroutine anónima
    go func() {
        fmt.Println("Goroutine anónima")
    }()

    // Esperar a que las goroutines terminen
    time.Sleep(time.Second)
}

Canales (Channels)

Comunicación entre goroutines usando canales.

// Crear canal
ch := make(chan int)
// Canal con buffer
buffered := make(chan string, 3)
// Enviar y recibir
go func() {
    ch <- 42  // Enviar valor
}()
value := <-ch  // Recibir valor
// Cerrar canal
close(ch)

Patrones de Canal

Patrones comunes para la comunicación por canal.

// Patrón de trabajador (Worker)
func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Trabajador %d procesando trabajo %d\n", id, job)
        results <- job * 2
    }
}
// Patrón Fan-out
jobs := make(chan int, 100)
results := make(chan int, 100)
// Iniciar trabajadores
for w := 1; w <= 3; w++ {
    go worker(w, jobs, results)
}
// Enviar trabajos
for j := 1; j <= 5; j++ {
    jobs <- j
}
close(jobs)

Sentencia Select

Maneja múltiples operaciones de canal simultáneamente.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "desde ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "desde ch2"
    }()

    // Seleccionar el canal disponible primero
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    case <-time.After(3 * time.Second):
        fmt.Println("tiempo de espera agotado")
    }
}

E/S de Archivos y JSON

Operaciones de Archivo

Leer y escribir archivos usando varios métodos.

import (
    "io/ioutil"
    "os"
)
// Leer archivo completo
data, err := ioutil.ReadFile("file.txt")
if err != nil {
    log.Fatal(err)
}
content := string(data)
// Escribir en archivo
text := "Hello, World!"
err = ioutil.WriteFile("output.txt", []byte(text), 0644)
// Abrir archivo con más control
file, err := os.Open("data.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

Manejo de CSV

Leer y escribir archivos CSV.

import (
    "encoding/csv"
    "os"
)
// Leer CSV
file, _ := os.Open("data.csv")
defer file.Close()
reader := csv.NewReader(file)
records, _ := reader.ReadAll()
// Escribir CSV
file, _ = os.Create("output.csv")
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
writer.Write([]string{"Nombre", "Edad", "Ciudad"})
writer.Write([]string{"Alice", "30", "NYC"})

Procesamiento JSON

Codificar y decodificar datos JSON.

import "encoding/json"
// Struct para mapeo JSON
type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email,omitempty"`
}
// Marshal (Go a JSON)
person := Person{Name: "Alice", Age: 30}
jsonData, err := json.Marshal(person)
if err != nil {
    log.Fatal(err)
}
// Unmarshal (JSON a Go)
var p Person
err = json.Unmarshal(jsonData, &p)

Solicitudes HTTP

Realizar solicitudes HTTP y manejar respuestas.

import "net/http"
// Solicitud GET
resp, err := http.Get("https://api.github.com/users/octocat")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// Solicitud POST con JSON
jsonData := []byte(`{"name":"Alice","age":30}`)
resp, err = http.Post("https://api.example.com/users",
                      "application/json",
                      bytes.NewBuffer(jsonData))

Pruebas (Testing)

Pruebas Unitarias: go test

Escribir y ejecutar pruebas usando el framework de pruebas de Go.

// math.go
package main
func Add(a, b int) int {
    return a + b
}
// math_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
        t.Errorf("Add(2, 3) = %d; se esperaba %d", result, expected)
    }
}
// Ejecutar pruebas
// go test
// go test -v (verboso)

Pruebas Basadas en Tablas (Table-Driven Tests)

Prueba múltiples casos de manera eficiente.

func TestAddMultiple(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"números positivos", 2, 3, 5},
        {"con cero", 0, 5, 5},
        {"números negativos", -1, -2, -3},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("obtuvo %d, se esperaba %d", result, tt.expected)
            }
        })
    }
}

Benchmarking

Mide el rendimiento de las funciones.

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}
// Ejecutar benchmarks
// go test -bench=.
// go test -bench=BenchmarkAdd -benchmem

Pruebas de Ejemplo (Example Tests)

Crea ejemplos ejecutables que sirven como documentación.

import "fmt"
func ExampleAdd() {
    result := Add(2, 3)
    fmt.Printf("2 + 3 = %d", result)
    // Output: 2 + 3 = 5
}
func ExampleAdd_negative() {
    result := Add(-1, -2)
    fmt.Printf("(-1) + (-2) = %d", result)
    // Output: (-1) + (-2) = -3
}
// Ejecutar ejemplos
// go test -run Example

Módulos y Paquetes de Go

Gestión de Módulos

Inicializa y gestiona módulos de Go para la gestión de dependencias.

# Inicializar nuevo módulo
go mod init github.com/username/project
# Añadir dependencias
go get github.com/gorilla/mux
go get -u github.com/gin-gonic/gin  # Actualizar a la última versión
# Eliminar dependencias no utilizadas
go mod tidy
# Descargar dependencias
go mod download
# Dependencias locales (vendor)
go mod vendor

Archivo go.mod

Entendiendo el archivo de definición del módulo.

module github.com/username/myproject
go 1.21
require (
    github.com/gorilla/mux v1.8.0
    github.com/stretchr/testify v1.8.4
)
require (
    github.com/davecgh/go-spew v1.1.1 // indirect
    github.com/pmezard/go-difflib v1.0.0 // indirect
)

Creación de Paquetes

Estructura el código en paquetes reutilizables.

// Estructura del paquete
// myproject/
//   ├── go.mod
//   ├── main.go
//   └── utils/
//       ├── math.go
//       └── string.go
// utils/math.go
package utils
// Función exportada (comienza con mayúscula)
func Add(a, b int) int {
    return a + b
}
// Función privada (comienza con minúscula)
func multiply(a, b int) int {
    return a * b
}
// main.go
package main
import (
    "fmt"
    "github.com/username/myproject/utils"
)
func main() {
    result := utils.Add(5, 3)
    fmt.Println(result)
}

Comandos Comunes de Go

Comandos esenciales para el desarrollo en Go.

# Ejecutar programa Go
go run main.go
# Compilar ejecutable
go build
go build -o myapp  # Nombre personalizado
# Instalar binario en GOPATH/bin
go install
# Formatear código
go fmt ./...
# Revisar código en busca de problemas
go vet ./...
# Limpiar caché de compilación
go clean -cache

Enlaces Relevantes