Saltar al contenido principal

Manejo de Errores

Los mecanismos de manejo de errores varían significativamente entre lenguajes. Así es como los diferentes lenguajes manejan errores y excepciones.

Manejo Básico de Errores​

// Try-catch
try {
riskyOperation();
} catch (error) {
console.error("Error:", error.message);
} finally {
cleanup();
}

// Throwing errors
throw new Error("Something went wrong");

// Custom errors
class CustomError extends Error {
constructor(message) {
super(message);
this.name = "CustomError";
}
}

Patrones Avanzados de Manejo de Errores​

// Promise error handling
fetch('/api/data')
.then(response => response.json())
.catch(error => console.error("Error:", error));

// Async/await
async function fetchData() {
try {
const response = await fetch('/api/data');
return await response.json();
} catch (error) {
console.error("Error:", error);
throw error;
}
}

// Error boundaries (React)
class ErrorBoundary extends React.Component {
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
}

Puntos Clave​

  • Excepciones vs retornos de error -- JavaScript, Python, PHP, Ruby, Java, C#, C++ y Swift usan excepciones estilo try/catch: haces throw o raise y opcionalmente catch más adelante. Go y Zig usan retornos de error explícitos: result, err := divide(10, 2) en Go y const result = divide(10, 2) catch |err| { ... } en Zig. Rust usa Result<T, E> y Option<T> en el sistema de tipos. C usa códigos de retorno (int divide(..., double* result) retornando 0 o -1) y errno. Las excepciones permiten que los errores se propaguen implícitamente a través de la pila de llamadas; los retornos de error obligan a cada punto de llamada a decidir. Elige excepciones cuando quieras un manejo centralizado; elige retornos de error cuando quieras un flujo de control explícito y menos sorpresas.

  • Tipos Result/Option -- Result<T, E> de Rust y las uniones de error !T de Zig codifican los errores en el sistema de tipos. Por ejemplo, fn divide(a: f64, b: f64) -> Result<f64, String> en Rust obliga a los llamadores a manejar Ok y Err mediante match, ? o .unwrap(). fn divide(a: f64, b: f64) !f64 en Zig retorna un valor o un error de un conjunto de errores en tiempo de compilación. Result<String, NetworkError> de Swift es opcional pero común para código asíncrono. (value, error) en Go es idiomático y se verifica con if err != nil. Los tipos Result hacen visible "esto puede fallar" en las firmas. Prefiere Rust o Zig cuando quieras garantías en tiempo de compilación de que los errores se manejan.

  • Manejo obligado por el compilador -- Rust y Zig requieren un manejo explícito de errores: debes usar match, ?, try o catch, o el código no compilará. Por ejemplo, let contents = File::open(path)?; en Rust propaga errores; ignorar el ? requeriría unwrap() o expect(), que es explícito. En Python, Java o JavaScript, puedes llamar a una función que lanza excepciones sin try/catch y el error se propagará (o fallará) silenciosamente hasta ser capturado. Si tu prioridad es eliminar errores no manejados en tiempo de compilación, elige Rust o Zig; si prefieres flexibilidad y manejadores centralizados, usa lenguajes basados en excepciones.

  • Propagación y envolvimiento de errores -- Go 1.13+ soporta fmt.Errorf("failed: %w", err) para envolver y errors.Is(err, ErrNotFound) para verificar. El operador ? de Rust propaga errores preservando la conversión mediante From. Python usa raise RuntimeError("failed") from e para encadenamiento. Java y C# soportan encadenamiento de excepciones con initCause. Envolver preserva el contexto cuando los errores ascienden. Elige lenguajes con buen soporte de envolvimiento cuando construyas sistemas en capas donde los errores cruzan muchos límites.

  • Limpieza de recursos -- RAII (C++), defer (Go, Zig), using (C#), try-with-resources (Java) y with (Python) aseguran la limpieza incluso cuando ocurren errores. Por ejemplo, defer file.close() en Zig y using var file = new FileStream(...) en C# garantizan la liberación. C no tiene un mecanismo incorporado; debes usar goto cleanup o condicionales anidados. Prefiere lenguajes con limpieza estructurada cuando gestiones archivos, conexiones o bloqueos para evitar fugas en rutas de error.