Panic, Defer y Recover
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 57 de 107.
Aunque Go hace hincapié en devolver errores, algunas situaciones son realmente irrecuperables. Para estos casos, Go proporciona panic, defer y recover: un mecanismo para manejar circunstancias excepcionales.
panic detiene inmediatamente la ejecución normal. Está reservado para errores de programación, como acceder a un índice fuera de los límites, no para errores esperados, como una entrada de usuario no válida:
func MustGetConfig(key string) string {
value, exists := config[key]
if !exists {
panic("missing required config: " + key)
}
return value
}defer programa una función para que se ejecute cuando la función que la contiene devuelva el control, ya sea normalmente o debido a un pánico. Las llamadas diferidas se ejecutan en orden inverso (última en entrar, primera en salir):
func ProcessFile() {
fmt.Println("Opening file")
defer fmt.Println("Closing file")
fmt.Println("Processing...")
// Salida: Opening file, Processing..., Closing file
}recover captura un pánico y devuelve la ejecución normal. Solo funciona dentro de una función defer:
func SafeOperation() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic: %v", r)
}
}()
riskyOperation()
return nil
}Este patrón convierte un pánico en un error normal, lo que permite que tu programa continúe. Úsalo con moderación: es preferible devolver errores en los casos de fallo esperados y reservar panic/recover para situaciones verdaderamente excepcionales, como un estado corrupto o condiciones irrecuperables.
Desafío
FácilConstruyamos una calculadora de división segura que demuestre cómo usar panic, defer y recover para manejar situaciones excepcionales de forma adecuada. Tu calculadora intentará realizar operaciones arriesgadas y convertirá los pánicos en errores normales que se puedan gestionar de manera habitual.
Organizarás tu código en dos archivos:
calculator.go: Crea la lógica de división con recuperación de pánicos.Implementa una función
Divideque reciba dos enteros y provoque un pánico con el mensaje"division by zero"si el divisor es cero. De lo contrario, devuelve el resultado entero de la división.Implementa una función
SafeDivideque reciba dos enteros y devuelva tanto un resultado entero como un error. Esta función debe:- Usar
defercon una función anónima para recuperarse de cualquier pánico - Si se recupera un pánico, convertirlo en un error con el formato
"calculation error: [panic message]" - Llamar internamente a la función
Divide - Devolver el resultado y
nilsi la operación tiene éxito, o cero y el error si se recuperó un pánico
- Usar
main.go: Lee dos enteros de la entrada y usaSafeDividepara realizar el cálculo de forma segura. También demuestradeferimprimiendo mensajes de limpieza en el orden correcto.Tu función principal debe:
- Imprimir
Starting calculational principio - Usar
deferpara programar la impresión deCleanup complete - Usar otro
deferpara programar la impresión deReleasing resources - Llamar a
SafeDividecon los valores de entrada - Imprimir
Result: [value]oError: [error message]según el resultado
- Imprimir
Se proporcionarán las siguientes entradas:
- Línea 1: Primer entero (dividendo)
- Línea 2: Segundo entero (divisor)
Recuerda que las llamadas diferidas se ejecutan en orden inverso (última en entrar, primera en salir), por lo que tus mensajes de limpieza deben aparecer en el orden opuesto a aquel en el que se difirieron.
Por ejemplo, dados 20 y 4, tu salida debe ser:
Starting calculation
Result: 5
Releasing resources
Cleanup completeY dados 10 y 0, tu salida debe ser:
Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup completePruébalo tú mismo
package main
import (
"fmt"
)
func main() {
// Leer entrada
var dividend, divisor int
fmt.Scanln(÷nd)
fmt.Scanln(&divisor)
// Imprimir mensaje de inicio
fmt.Println("Starting calculation")
// TODO: Use defer to schedule "Cleanup complete" message
// TODO: Use defer to schedule "Releasing resources" message
// Recuerda: las llamadas diferidas se ejecutan en orden LIFO (last-in, first-out)
// TODO: Llama a SafeDivide con los valores de entrada
// TODO: Imprime "Result: [value]" o "Error: [error message]"
// según si se devolvió un error
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker PoolPractica por tu cuenta: Compilador de Go online