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 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.

challenge icon

Desafío

Fácil

Construyamos 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 Divide que 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 SafeDivide que reciba 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 internamente a la función Divide
    • Devolver el resultado y nil si la operación 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 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 (ú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 complete

Y dados 10 y 0, tu salida debe ser:

Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete
FORMATO DE SALIDA REQUERIDO: [Tu contenido traducido aquí]

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: 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
}
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

Practica por tu cuenta: Compilador de Go online