GoSuda

¿Por qué Go no tiene Try-Catch?

By Rabbit Princess
views ...

Go, intencionadamente, no soporta try-catch y solo soporta la sintaxis panic-recover. Esto ha generado descontento entre muchos desarrolladores acostumbrados al manejo de errores con try-catch. Entonces, ¿por qué no se incluye try-catch? La razón es que try-catch presenta varios problemas.

try-catch-finally

La construcción try-catch es una estructura para manejar situaciones de error y excepción que pueden ocurrir durante la ejecución del programa (runtime). Además, el bloque finally contiene el código que debe ejecutarse incondicionalmente, independientemente de si ocurre una excepción.

Manejo de errores y responsabilidad

En las décadas de los 80 y 90, el manejo de errores era muy simple. Los mensajes de error se limitaban a 'Disco flexible lleno', 'No hay disco flexible en la unidad', 'No hay permisos de escritura en el disco flexible', y los desarrolladores de esa época, al producirse un error, lo lanzaban (throw) hasta un punto común de manejo de errores para su procesamiento. En estas circunstancias, la construcción try-catch funcionaba eficientemente.

Sin embargo, con el tiempo, la situación cambió. La lógica de negocio se hizo más compleja, surgieron las bases de datos y las transacciones, se tuvieron que interpretar innumerables mensajes de solicitud al llamar a numerosas API a través de la red, e incluso con la aparición de la programación concurrente, los errores debían manejarse en hilos diferentes al principal.

Los errores se volvieron tan complejos que ya no podían manejarse en un solo lugar, y ningún punto podía asumir la responsabilidad de todos los errores. Aquí es donde surge un problema grave con try-catch.

Traslado de responsabilidad de try-catch

Try-catch es, en pocas palabras, un método por el cual la entidad que genera un error traslada la responsabilidad (el manejo posterior) de dicho error a otra. Este destinatario puede ser el bloque catch, su método padre, o el padre del padre del padre del padre... de alguien. En otras palabras, en un mundo donde el manejo de errores se vuelve más numeroso y complejo, el método elegido por try-catch es simplemente 'alguien lo hará'. Veamos el siguiente código.

1try {
2    data = readFile("hello.txt"); // leer el archivo "hello.txt"
3    structuredData = parseData(data); // analizar los datos
4    insertDBStatus(structuredData[1]); // insertar el estado en la base de datos
5    startHTTPServer(structuredData[2]); // iniciar el servidor HTTP
6} catch (Exception e) {
7    e.printStackTrace(); // imprimir la traza de la pila
8}

El problema con el código anterior es que no está claro quién es el responsable de procesar printStackTrace, y tampoco se puede saber en qué parte del código se produjo el error. Además, cuanto más lógica se añade a la sentencia try, el problema se vuelve aún más terrible. Sin embargo, paradójicamente, a medida que el desarrollo se volvía más complejo, los desarrolladores se hicieron adictos a la construcción try-catch que traslada la responsabilidad, dejaron de preocuparse por el manejo de errores, su sentido de responsabilidad se diluyó, y finalmente olvidaron la esencia del manejo de errores y excepciones. Entonces, ¿cómo resolvió Go este problema?

panic, recover

Una de las ventajas de Go es que cuenta con varios sistemas para guiar a los desarrolladores hacia buenas prácticas, en lugar de desviarlos por malos caminos. Panic-recover es un ejemplo de ello. ​ A primera vista, panic y recover pueden parecer no muy diferentes de try-catch, pero se distinguen en que, cuando surge un problema, no trasladan la responsabilidad al exterior. En Go, cuando ocurre un panic, el valor debe resolverse en la ubicación donde se produjo el panic, en lugar de pasarlo al exterior. Esto asigna la responsabilidad del manejo de errores al desarrollador, incitándolo a considerar más profundamente dónde, quién y cómo debe manejarse dicho error. De esta manera, se garantiza la autonomía del usuario, al tiempo que se deja espacio para la reflexión.

Además, la elección de la palabra "panic" es muy acertada, ya que a diferencia de try-recover, panic-recover impone al desarrollador la presión de usarlo solo en situaciones de error claras, no en situaciones de excepción, solo por la palabra en sí. Naturalmente, el desarrollador no abusa de recover y lo utiliza solo donde es necesario. Esto contribuye en gran medida a escribir código conciso en Go.