Conociendo Panic y Recover en Go (Golang)

in #cervantes6 years ago

gopher wallpaper

Panic y Recover en Go

En la publicación dedicada a los errores hablamos superficialmente de panic y me comprometí a profundizar más sobre el tema en una publicación futuras. El futuro es hoy, conozcamos a panic y también a recover

Panic

Podemos considerar un panic como un error que detendrá la ejecución de nuestro programa a menos que lo manejemos. Los errores de los que hablamos anteriormente nos permitían comprobar su existencia y tomar las acciones que consideremos adecuadas según la situación. En el caso de producirse un panic el programa se detendrá desde la función donde se originó hacia arriba, en el caso de que no sea esta nuestra intención, debemos tomar medidas para manejar el panic, pero de esto último hablaremos más adelante. Veamos un ejemplo común.

package main

import "fmt"

func main() {
    slice := []int{3, 4, 5, 6, 8}
    fmt.Println(slice[3]) // 6
    fmt.Println(slice[7]) // panic: runtime error: index out of range

    fmt.Println("Fin") // no se ejecuta
}



Como podemos ver, intentar acceder a un indice fuera del rango de un slice produce un panic, lo cual termina la ejecución del programa, por tal motivo la instrucción fmt.Println("Fin") no llega a ejecutarse. Como esta existen diversas acciones en Go que pueden producir un panic, aunque no es la única manera.

Generando nuestro propio panic

También es posible generar nuestros propios panic si así lo deseamos. Para esto hacemos uso de la función panic, la cual recibe una interfaz, es decir, que básicamente podemos pasar cualquier tipo de dato.

package main

import (
    "fmt"
    "strconv"
)

func main() {
    var cadena = "j"
    numero, err := strconv.Atoi(cadena)
    if err != nil {
        panic(err)
    }
    fmt.Println(numero + 10)
}



Es posible que este código te resulte familiar, fue uno de los ejemplos de la publicación sobre manejo de errores. Con la instrucción panic(err) provocamos un panic con el error contenido en la variable err. Si ejecutas el programa deberías obtener panic: strconv.Atoi: parsing "j": invalid syntax, que corresponde al error que definimos antes. Ahora vamos a refactorizar un poco el código para entender el comportamiento de panic.

package main

import (
    "fmt"
    "strconv"
)

func cadenaANumero(cadena string) int {
    numero, err := strconv.Atoi(cadena)
    if err != nil {
        panic(err)
    }
    return numero
}

func main() {
    fmt.Println("Inicia")           // Inicia
    fmt.Println(cadenaANumero("2")) // 2
    fmt.Println(cadenaANumero("j")) // panic: strconv.Atoi: parsing "j": invalid syntax
    fmt.Println("Fin")              // No se ejecuta
}



Ahora tenemos todo el proceso de conversión de cadena de texto a número entero en una función separada llamada cadenaANumero y procedemos a llamarla desde la función main. Al momento de pasar una cadena de texto que no es posible convertir a número entero se produce un error, ejecutando el panic que se encuentra dentro del condicional en la función cadenaANumero.

Lo que deseo que noten es que el panic se produce en la función externa cadenaANumero, detiene la ejecución de la misma y termina también con la ejecución de la función main, por tal motivo no llega a ejecutarse la instrucción fmt.Println("Fin").

Recover

Ahora que entendemos como funciona panic, veamos como evitar que termine con la ejecución de nuestro programa. Para esto Go nos provee de la función recover, y podemos ejecutarla de la siguiente manera.

...
func main() {
    fmt.Println("Inicia")           // Inicia
    fmt.Println(cadenaANumero("2")) // 2
    fmt.Println(cadenaANumero("j")) // panic: strconv.Atoi: parsing "j": invalid syntax
    recuperado := recover()
    fmt.Println(recuperado)
    fmt.Println("Fin") // No se ejecuta
}



Si ejecutan este código notarán que no hay cambios en la salida respecto al anterior. El hecho es que panic detiene la ejecución de la función antes de que recover llegue a ejecutarse. Para solucionar eso podemos recurrir a la vieja conocida defer.

package main

import (
    "fmt"
    "strconv"
)

func cadenaANumero(cadena string) int {
    defer func() {
        recuperado := recover()
        if recuperado != nil {
            fmt.Println(recuperado)
        }
    }()
    numero, err := strconv.Atoi(cadena)
    if err != nil {
        panic(err)
    }
    return numero
}

func main() {
    fmt.Println("Inicia")           // Inicia
    fmt.Println(cadenaANumero("2")) // 2
    // strconv.Atoi: parsing "j": invalid syntax
    fmt.Println(cadenaANumero("j")) // 0
    fmt.Println("Fin")              // Fin
}



En este caso el panic aún terminará con la ejecución de la función cadenaANumero, pero esta vez lo estamos manejando y este no se propagará a funciones superiores. Es por este motivo que se llaga a imprimir Fin en la terminal de comandos.

Por si no queda claro, el condicional if recuperado != nil sólo evita que se imprima el valor de recuperado cuando la función se ejecute correctamente, es decir, cuando recover() retorne nil. De igual forma, la salida strconv.Atoi: parsing "j": invalid syntax es el valor de recuperado obtenido del panic el cual se manda a imprimir desde la función cadenaANumero.

separator.png

Publicaciones relacionadas

  1. De Python a Go (Golang)

  2. Introducción al lenguaje de programación Go (Golang)

  3. Estructuras de control de flujo en Go

  4. Array y Slice en Go (Golang)

  5. Maps en Go (Golang)

  6. Punteros en Go (Golang)

  7. Importaciones y paquetes en Go (Golang)

  8. Paquetes de terceros y alias en Go (Golang)

  9. Tipos de datos Personalizados y Métodos en Go (Golang)

  10. Estructuras: ¿Las Clases de Go (Golang)?

  11. Interfaces en Go (Golang)

  12. Interfaces vacías en Go (Golang)

  13. Manejo de errores en Go (Golang)

Gracias por leer, espero que este artículo te resultara de provecho. Si así fue, no dudes en dejar un comentario, compartirlo y votar. Te invito a comentar cualquier duda o sugerencia, te aseguro que las leo todas. Así que, por favor, ayúdame a mejorar y continuar compartiendo contenido de calidad. Si te gusta la programación y/o la informática en general, te invito a formar parte de la comunidad Develop Spanish dónde compartimos contenido de esa naturaleza y totalmente en español. Hasta la próxima.

banner-steemit.jpg

Sort:  

Hello! Your post has been resteemed and upvoted by @ilovecoding because we love coding! Keep up good work! Consider upvoting this comment to support the @ilovecoding and increase your future rewards! ^_^ Steem On!

Reply !stop to disable the comment. Thanks!