Menu
Coddy logo textTech

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 enfatiza el retorno de errores, algunas situaciones son verdaderamente irrecuperables. Para estos casos, Go proporciona panic, defer, y recover—un mecanismo para manejar circunstancias excepcionales.

panic detiene la ejecución normal inmediatamente. 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 invá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 rodea retorna, ya sea de forma normal o debido a un pánico (panic). Las llamadas diferidas se ejecutan en orden inverso (último en entrar, primero 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 retoma la ejecución normal. Solo funciona dentro de una función diferida:

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 panic en un error regular, permitiendo que su programa continúe. Úselo con moderación: prefiera devolver errores para los casos de falla esperados y reserve panic/recover para situaciones verdaderamente excepcionales, como un estado corrupto o condiciones irrecuperables.

challenge icon

Desafío

Fácil

Vamos a construir una calculadora de división segura que demuestre cómo usar panic, defer y recover para manejar situaciones excepcionales con elegancia. Tu calculadora intentará realizar operaciones arriesgadas y convertirá los pánicos en errores regulares que pueden ser manejados normalmente.

Organizarás tu código en dos archivos:

  • calculator.go: Crea la lógica de división con recuperación de pánico.

    Implementa una función Divide que tome dos enteros y lance 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 SafeDivide que tome dos enteros y devuelva tanto un resultado entero como un error. Esta función debe:

    • Usar defer con 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 a la función Divide internamente
    • Devolver el resultado y nil si tiene éxito, o cero y el error si se recuperó un pánico
  • main.go: Lee dos enteros de la entrada y usa SafeDivide para realizar el cálculo de forma segura. También demuestra defer imprimiendo mensajes de limpieza en el orden correcto.

    Tu función principal debe:

    • Imprimir Starting calculation al principio
    • Usar defer para programar la impresión de Cleanup complete
    • Usar otro defer para programar la impresión de Releasing resources
    • Llamar a SafeDivide con los valores de entrada
    • Imprimir ya sea Result: [value] o Error: [error message] según el resultado

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 (último en entrar, primero en salir), por lo que tus mensajes de limpieza deben aparecer en el orden opuesto a como fueron diferidos.

Por ejemplo, dados 20 y 4, tu salida debería ser:

Starting calculation
Result: 5
Releasing resources
Cleanup complete

Y dados 10 y 0, tu salida debería ser:

Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete

Hoja de referencia

Go proporciona panic, defer, y recover para manejar circunstancias excepcionales.

panic detiene la ejecución normal inmediatamente y está reservado para errores de programación irrecuperables:

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 circundante retorna. Las llamadas diferidas se ejecutan en orden inverso (último en entrar, primero 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 restaura la ejecución normal. Solo funciona dentro de una función diferida:

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 regular. Es preferible devolver errores para los fallos esperados y reservar panic/recover para situaciones verdaderamente excepcionales.

Pruébalo tú mismo

package main

import (
	"fmt"
)

func main() {
	// Leer entrada
	var dividend, divisor int
	fmt.Scanln(&dividend)
	fmt.Scanln(&divisor)

	// Imprimir mensaje de inicio
	fmt.Println("Starting calculation")

	// TODO: Usar defer para programar el mensaje "Cleanup complete"

	// TODO: Usar defer para programar el mensaje "Releasing resources"
	// Recordatorio: las llamadas diferidas se ejecutan en orden LIFO (last-in, first-out)

	// TODO: Llamar a SafeDivide con los valores de entrada

	// TODO: Imprimir "Result: [value]" o "Error: [error message]"
	// basado en si se devolvió un error
}
quiz iconPonte a prueba

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