# Paquetes Importantes de Go: context, io, fmt, sync y reflect

En los posts anteriores exploramos los fundamentos de Go: structs, interfaces, métodos, collections y manejo de errores. Hoy vamos a profundizar en los **paquetes más importantes de la biblioteca estándar de Go**. Estos paquetes son fundamentales para escribir código Go profesional y entenderlos te ayudará a aprovechar al máximo el lenguaje.

Go viene con una biblioteca estándar muy completa y bien diseñada. A diferencia de Java, donde necesitas muchas librerías externas, Go te proporciona herramientas poderosas desde el inicio.

## **context: Cancelación y Timeouts**

El paquete `context` es uno de los más importantes en Go. Proporciona un mecanismo para **cancelación**, **timeouts** y **valores** que se propagan a través de llamadas de función y goroutines.

### **¿Por qué es Importante?**

En aplicaciones concurrentes y distribuidas, necesitas:

* Cancelar operaciones que ya no son necesarias
    
* Establecer timeouts para evitar que operaciones bloqueen indefinidamente
    
* Pasar valores (como request IDs) a través de múltiples capas
    

### **Context con Timeout**

```go
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// Simular operación que puede tardar
done := make(chan bool)
go func() {
    time.Sleep(3 * time.Second) // Tarda más que el timeout
    done <- true
}()

select {
case <-done:
    fmt.Println("Operation completed")
case <-ctx.Done():
    fmt.Printf("Operation cancelled: %v\n", ctx.Err())
    // Output: Operation cancelled: context deadline exceeded
}
```

### **Context con Cancelación Manual**

```go
ctx, cancel := context.WithCancel(context.Background())

go func() {
    time.Sleep(1 * time.Second)
    cancel() // Cancelar después de 1 segundo
}()

<-ctx.Done()
fmt.Printf("Context cancelled: %v\n", ctx.Err())
```

### **Context con Valores**

```go
// Agregar valores al context
ctx := context.WithValue(context.Background(), "userID", 123)
ctx = context.WithValue(ctx, "requestID", "req-456")

// Recuperar valores
userID := ctx.Value("userID").(int)
requestID := ctx.Value("requestID").(string)
```

**⚠️ Advertencia**: Usa valores en context solo para datos de request-scope (como request IDs, user IDs). No uses context como un contenedor de parámetros general.

### **Uso en HTTP y Database**

```go
// En HTTP handlers
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context() // Obtener context del request
    
    // Pasar context a operaciones de base de datos
    user, err := h.repo.GetUser(ctx, userID)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    // ...
}

// En operaciones de base de datos
func (r *Repository) GetUser(ctx context.Context, id int) (*User, error) {
    // El context permite cancelación y timeout
    query := "SELECT * FROM users WHERE id = ?"
    row := r.db.QueryRowContext(ctx, query, id)
    // ...
}
```

## **io: Operaciones de Entrada/Salida**

El paquete `io` proporciona interfaces y funciones para trabajar con I/O de forma genérica. Es similar a `java.io` pero más simple y poderoso gracias a las interfaces de Go.

### **Interfaces Principales**

```go
// Reader - para leer datos
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Writer - para escribir datos
type Writer interface {
    Write(p []byte) (n int, err error)
}

// Closer - para cerrar recursos
type Closer interface {
    Close() error
}
```

### **Leer de un Archivo**

```go
file, err := os.Open("data.txt")
if err != nil {
    return err
}
defer file.Close()

// Leer todo el contenido
data, err := io.ReadAll(file)
if err != nil {
    return err
}
fmt.Printf("Read %d bytes\n", len(data))
```

### **Escribir a un Writer**

```go
// Escribir string directamente
io.WriteString(os.Stdout, "Hello from io.WriteString!\n")

// Escribir a cualquier Writer
var buf bytes.Buffer
io.WriteString(&buf, "Hello")
```

### **Copiar entre Reader y Writer**

```go
// Copiar de un Reader a un Writer
file, _ := os.Open("source.txt")
defer file.Close()

written, err := io.Copy(os.Stdout, file)
if err != nil {
    return err
}
fmt.Printf("Copied %d bytes\n", written)
```

### **Ventaja de las Interfaces**

La belleza de `io.Reader` y `io.Writer` es que funcionan con **cualquier tipo** que implemente estas interfaces:

* Archivos (`os.File`)
    
* Sockets de red (`net.Conn`)
    
* Buffers en memoria (`bytes.Buffer`)
    
* Strings (`strings.Reader`)
    
* Cualquier tipo personalizado que implemente las interfaces
    

```go
// Esta función funciona con CUALQUIER Reader
func processData(r io.Reader) error {
    data, err := io.ReadAll(r)
    // Procesar data...
    return err
}

// Puedes pasar diferentes tipos:
processData(file)           // Archivo
processData(conn)           // Conexión de red
processData(&buf)           // Buffer
processData(strings.NewReader("data")) // String
```

## **fmt: Formateo e Impresión**

El paquete `fmt` es similar a `System.out.println` y `String.format` en Java, pero más poderoso y flexible.

### **Printf: Formateo con Especificadores**

```go
name := "John"
age := 30
balance := 1234.56

fmt.Printf("Name: %s, Age: %d, Balance: $%.2f\n", name, age, balance)
// Output: Name: John, Age: 30, Balance: $1234.56
```

**Especificadores comunes:**

* `%s` - string
    
* `%d` - entero decimal
    
* `%f` - float (`.2f` para 2 decimales)
    
* `%v` - valor en formato por defecto
    
* `%+v` - struct con nombres de campos
    
* `%#v` - representación Go-syntax
    
* `%T` - tipo del valor
    

### **Sprintf: Formateo a String**

```go
message := fmt.Sprintf("User %s is %d years old", name, age)
// message = "User John is 30 years old"
```

### **Fprintf: Escribir a un Writer**

```go
// Escribir a stderr
fmt.Fprintf(os.Stderr, "Error: %v\n", err)

// Escribir a un archivo
file, _ := os.Create("output.txt")
fmt.Fprintf(file, "Data: %s\n", data)
```

### **Scanf: Leer Entrada Formateada**

```go
var name string
var age int
fmt.Scanf("%s %d", &name, &age)
```

**Nota**: `fmt.Scanf` es útil para programas simples, pero para entrada interactiva compleja, considera usar `bufio.Scanner`.

### **Implementar Stringer**

Si tu tipo implementa la interfaz `Stringer`, `fmt` la usará automáticamente:

```go
type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("Person{Name: %s, Age: %d}", p.Name, p.Age)
}

p := Person{Name: "John", Age: 30}
fmt.Println(p) // Output: Person{Name: John, Age: 30}
```

## **sync: Sincronización y Concurrencia**

El paquete `sync` proporciona primitivas de sincronización para código concurrente. Es similar a `java.util.concurrent` pero más simple y directo.

### **Mutex: Exclusión Mutua**

```go
var mu sync.Mutex
counter := 0

increment := func() {
    mu.Lock()
    defer mu.Unlock() // Siempre usar defer para unlock
    counter++
}

// Múltiples goroutines pueden llamar increment() de forma segura
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        increment()
    }()
}
wg.Wait()
fmt.Printf("Counter: %d\n", counter) // 10
```

**Regla de oro**: Siempre usa `defer mu.Unlock()` para evitar deadlocks.

### **RWMutex: Lectura/Escritura**

`RWMutex` permite múltiples lectores simultáneos o un escritor exclusivo:

```go
var rwmu sync.RWMutex
data := make(map[string]int)

// Escritura (exclusiva)
func write(key string, value int) {
    rwmu.Lock()
    defer rwmu.Unlock()
    data[key] = value
}

// Lectura (múltiples lectores simultáneos permitidos)
func read(key string) int {
    rwmu.RLock()
    defer rwmu.RUnlock()
    return data[key]
}
```

**Cuándo usar RWMutex**: Cuando tienes muchos lectores y pocos escritores. Si tienes muchos escritores, `Mutex` regular puede ser más eficiente.

### **WaitGroup: Esperar Múltiples Goroutines**

```go
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1) // Incrementar contador
    go func(id int) {
        defer wg.Done() // Decrementar cuando termine
        time.Sleep(100 * time.Millisecond)
        fmt.Printf("Goroutine %d completed\n", id)
    }(i)
}

wg.Wait() // Esperar a que todas terminen
fmt.Println("All goroutines completed")
```

**Patrón común**: `Add()` antes de lanzar la goroutine, `Done()` al finalizar (usando `defer`).

### **Once: Ejecutar Solo Una Vez**

```go
var once sync.Once
initialize := func() {
    fmt.Println("Initializing (should only see this once)")
}

// Llamar múltiples veces
once.Do(initialize)
once.Do(initialize)
once.Do(initialize)
// Output: Initializing (should only see this once)
```

Útil para inicialización lazy thread-safe.

### **Otras Primitivas de sync**

* `sync.Pool`: Pool de objetos reutilizables para reducir allocations
    
* `sync.Cond`: Condition variables para señalización entre goroutines
    
* `sync.Map`: Map thread-safe (usa solo cuando realmente lo necesites)
    

## **reflect: Reflexión en Tiempo de Ejecución**

El paquete `reflect` permite inspeccionar tipos y valores en tiempo de ejecución. Es similar a `java.lang.reflect` pero más limitado y con mejor performance.

### **⚠️ Advertencia**

**Usa** `reflect` con moderación. Es poderoso pero:

* Más lento que código directo
    
* Menos type-safe
    
* Más difícil de entender y mantener
    

Úsalo solo cuando realmente lo necesites (serialización, frameworks, etc.).

### **Inspeccionar Tipos**

```go
var x int = 42
t := reflect.TypeOf(x)
fmt.Printf("Type: %s\n", t) // int

v := reflect.ValueOf(x)
fmt.Printf("Value: %v\n", v.Int()) // 42
```

### **Inspeccionar Structs**

```go
type Person struct {
    Name string
    Age  int
}

p := Person{Name: "John", Age: 30}
pType := reflect.TypeOf(p)
pValue := reflect.ValueOf(p)

fmt.Printf("Struct type: %s\n", pType)
fmt.Printf("Number of fields: %d\n", pType.NumField())

for i := 0; i < pType.NumField(); i++ {
    field := pType.Field(i)
    value := pValue.Field(i)
    fmt.Printf("  Field %d: %s (%s) = %v\n", 
        i, field.Name, field.Type, value.Interface())
}
```

### **Modificar Valores (Requiere Puntero)**

```go
p := &Person{Name: "Jane", Age: 25}
pValue := reflect.ValueOf(p).Elem() // Elem() obtiene el valor apuntado
ageField := pValue.FieldByName("Age")

if ageField.CanSet() {
    ageField.SetInt(26)
    fmt.Printf("Modified age: %d\n", p.Age) // 26
}
```

### **Casos de Uso Comunes**

* **Serialización/Deserialización**: JSON, XML, etc.
    
* **Frameworks**: ORMs, validators
    
* **Testing**: Comparación de valores complejos
    
* **Code generation**: Herramientas que generan código
    

## **Ejemplo Práctico: Combinando Múltiples Paquetes**

Vamos a construir un `DataProcessor` que combina varios paquetes:

```go
type DataProcessor struct {
    mu     sync.RWMutex           // sync: sincronización
    data   map[string]interface{} // Datos
    ctx    context.Context        // context: cancelación
    cancel context.CancelFunc
}

func NewDataProcessor() *DataProcessor {
    ctx, cancel := context.WithCancel(context.Background())
    return &DataProcessor{
        data:   make(map[string]interface{}),
        ctx:    ctx,
        cancel: cancel,
    }
}

func (dp *DataProcessor) Set(key string, value interface{}) {
    dp.mu.Lock()
    defer dp.mu.Unlock()
    dp.data[key] = value
}

func (dp *DataProcessor) Get(key string) (interface{}, bool) {
    dp.mu.RLock()
    defer dp.mu.RUnlock()
    value, exists := dp.data[key]
    return value, exists
}

func (dp *DataProcessor) ProcessWithTimeout(timeout time.Duration) error {
    ctx, cancel := context.WithTimeout(dp.ctx, timeout)
    defer cancel()

    done := make(chan error)
    go func() {
        time.Sleep(2 * time.Second)
        done <- nil
    }()

    select {
    case err := <-done:
        return err
    case <-ctx.Done():
        return ctx.Err()
    }
}

func (dp *DataProcessor) Stop() {
    dp.cancel()
}
```

Este ejemplo muestra:

* **sync.RWMutex**: Para acceso thread-safe
    
* **context**: Para cancelación y timeouts
    
* **Channels**: Para comunicación entre goroutines
    

## **Comparación: Java vs Go**

| **Paquete Go** | **Equivalente Java** | **Diferencia Clave** |
| --- | --- | --- |
| `context` | `CompletableFuture`, `ExecutorService` | Más integrado, más simple |
| `io` | `java.io` | Interfaces más pequeñas y composables |
| `fmt` | `System.out`, `String.format` | Más flexible, mejor integrado |
| `sync` | `java.util.concurrent` | Más simple, menos overhead |
| `reflect` | `java.lang.reflect` | Más limitado pero más rápido |

## **Mejores Prácticas**

### **1\. Siempre Pasa Context en Operaciones Largas**

```go
// ✅ Bueno
func (r *Repository) GetUser(ctx context.Context, id int) (*User, error) {
    return r.db.QueryRowContext(ctx, "SELECT ...", id)
}

// ❌ Evitar (sin cancelación posible)
func (r *Repository) GetUser(id int) (*User, error) {
    return r.db.QueryRow("SELECT ...", id)
}
```

### **2\. Usa Interfaces de io en Lugar de Tipos Concretos**

```go
// ✅ Bueno - acepta cualquier Reader
func processData(r io.Reader) error {
    data, err := io.ReadAll(r)
    // ...
}

// ❌ Menos flexible
func processData(file *os.File) error {
    // Solo acepta archivos
}
```

### **3\. Siempre Usa defer con Mutex**

```go
// ✅ Bueno
mu.Lock()
defer mu.Unlock()
// código...

// ❌ Peligroso - fácil olvidar unlock
mu.Lock()
// código...
mu.Unlock()
```

### **4\. Evita reflect a Menos que Sea Necesario**

```go
// ✅ Preferir código directo
func getAge(p Person) int {
    return p.Age
}

// ❌ Evitar reflect si no es necesario
func getAge(p Person) int {
    v := reflect.ValueOf(p)
    return int(v.FieldByName("Age").Int())
}
```

### **5\. Usa fmt.Sprintf para Construir Mensajes de Error**

```go
// ✅ Bueno
return fmt.Errorf("failed to process user %d: %w", userID, err)

// ❌ Menos informativo
return errors.New("failed to process user")
```

## **Errores Comunes**

### **❌ Error 1: Olvidar Cancelar Context**

```go
// ❌ MAL - leak de recursos
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// Olvidar cancel()

// ✅ BIEN - siempre cancelar
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
```

### **❌ Error 2: No Usar defer con Mutex**

```go
// ❌ MAL - puede causar deadlock
mu.Lock()
if condition {
    return // Olvidó unlock!
}
mu.Unlock()

// ✅ BIEN
mu.Lock()
defer mu.Unlock()
if condition {
    return // Unlock automático
}
```

### **❌ Error 3: Modificar reflect.Value sin CanSet()**

```go
// ❌ MAL - panic
p := Person{Name: "John"}
v := reflect.ValueOf(p)
v.FieldByName("Age").SetInt(30) // Panic!

// ✅ BIEN - usar puntero
p := &Person{Name: "John"}
v := reflect.ValueOf(p).Elem()
if v.FieldByName("Age").CanSet() {
    v.FieldByName("Age").SetInt(30)
}
```

## **Conclusiones**

Los paquetes estándar de Go son poderosos y bien diseñados:

✅ **context**: Esencial para cancelación y timeouts en código concurrente

✅ **io**: Interfaces simples y composables para I/O genérico

✅ **fmt**: Formateo flexible e integrado con el lenguaje

✅ **sync**: Primitivas de sincronización simples y eficientes

✅ **reflect**: Poderoso pero úsalo con moderación

**Conceptos clave a recordar:**

* Siempre pasa `context` en operaciones que pueden cancelarse
    
* Usa interfaces de `io` para código más flexible
    
* `defer` con mutex es tu amigo
    
* `reflect` es útil pero costoso - úsalo solo cuando sea necesario
    
* La biblioteca estándar de Go es muy completa - aprende a usarla bien
    

Dominar estos paquetes te permitirá escribir código Go profesional, eficiente y mantenible. Son la base sobre la cual se construyen aplicaciones Go reales.

## **Próximos Pasos**

En el siguiente post comenzaremos a explorar la **concurrencia en Go**: goroutines, channels, select, y más. Este es uno de los aspectos más poderosos y distintivos de Go, y entenderlo bien es crucial para alcanzar nivel Senior.

---

**¿Tienes preguntas sobre estos paquetes estándar de Go?** Déjame saber en los comentarios. Y si quieres ver el código completo de estos ejemplos, puedes encontrarlo en mi repositorio [go-mastery-lab](https://github.com/tu-usuario/go-mastery-lab).

Enjoy!

José Díaz

+51 939965148
