Manejo de errores en Go: try/catch y más de la A a la Z

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Manejo de errores en Go: filosofía y práctica



A diferencia de muchos lenguajes modernos, Go (Golang) no utiliza la construcción clásica try/catch. En su lugar, los desarrolladores de Go siguen el enfoque de «errores como valores» (errors as values). Esto hace que el código sea más explícito, predecible y seguro. En este artículo analizaremos todos los aspectos del manejo de errores en Go: desde patrones básicos hasta técnicas avanzadas con defer, panic y recover.



1. Fundamentos: los errores son simplemente valores



En Go, un error es una interfaz error con un único método Error() string. Cualquier función que pueda fallar devuelve un error como último valor de retorno.



package main

import ( "errors" "fmt")

// Función de división con retorno de errorfunc divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("división por cero no es posible") } return a / b, nil}

func main() { result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) return } fmt.Println("Resultado:", result)}


Punto clave: la comprobación if err != nil es la forma estándar de manejar errores en Go. No hay excepciones ocultas que puedan «saltar» inesperadamente.



2. Creación y personalización de errores



Go proporciona varias formas de crear errores:



  • errors.New() — error de texto simple.
  • fmt.Errorf() — error con formato (soporta %w para wrapping).
  • Tipos personalizados — para transmitir contexto adicional.


Error personalizado con datos adicionales



type DivisionError struct {    Dividend float64    Divisor  float64    Message  string}

func (e *DivisionError) Error() string { return fmt.Sprintf("%s: %f / %f", e.Message, e.Dividend, e.Divisor)}

func safeDivide(a, b float64) (float64, error) { if b == 0 { return 0, &DivisionError{ Dividend: a, Divisor: b, Message: "división por cero", } } return a / b, nil}

func main() { _, err := safeDivide(10, 0) if err != nil { // Type assertion para obtener campos adicionales var divErr *DivisionError if errors.As(err, &divErr) { fmt.Printf("Error con %f sobre %f: %s", divErr.Dividend, divErr.Divisor, divErr.Message) } }}


El uso de errors.As() permite verificar si un error es de un tipo específico y extraer datos del mismo. Es el equivalente de instanceof en otros lenguajes.



3. Patrones de manejo de errores: de lo simple a lo complejo



En Go existen varios patrones establecidos para trabajar con errores:



3.1. Sentinel errors (errores centinela)

Son errores predefinidos que se pueden comparar con ==. Ejemplo: io.EOF.



import (    "errors"    "fmt")

var ErrNotFound = errors.New("elemento no encontrado")

func findUser(id int) (string, error) { if id != 1 { return "", ErrNotFound } return "Alice", nil}

func main() { _, err := findUser(2) if errors.Is(err, ErrNotFound) { fmt.Println("Usuario no encontrado, creando uno nuevo...") }}


3.2. Wrapping de errores (envoltura)

A partir de Go 1.13, se pueden envolver errores usando %w en fmt.Errorf. Esto preserva la cadena de errores.



func readConfig(path string) error {    data, err := os.ReadFile(path)    if err != nil {        return fmt.Errorf("lectura del config %s: %w", path, err)    }    // ... procesamiento de data                

Blogs

Book Recommendations