Conociendo Panic y Recover en Go (Golang)
cervantes·@orlmicron·
0.000 HBDConociendo Panic y Recover en Go (Golang)
 # Panic y Recover en Go *En la publicación dedicada a los [errores](https://steemit.com/cervantes/@orlmicron/manejo-de-errores-en-go-golang) 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. ```go 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 } ``` <br /> 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. ```go package main import ( "fmt" "strconv" ) func main() { var cadena = "j" numero, err := strconv.Atoi(cadena) if err != nil { panic(err) } fmt.Println(numero + 10) } ``` <br /> Es posible que este código te resulte familiar, fue uno de los ejemplos de la publicación sobre [manejo de errores](https://steemit.com/cervantes/@orlmicron/manejo-de-errores-en-go-golang). 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`. ```go 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 } ``` <br /> 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. ```go ... 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 } ``` <br /> 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`. ```go 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 } ``` <br /> 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`. <center></center> ## Publicaciones relacionadas 1. [De Python a Go (Golang)](https://steemit.com/cervantes/@orlmicron/de-python-a-go-golang) 2. [Introducción al lenguaje de programación Go (Golang)](https://steemit.com/cervantes/@orlmicron/introduccion-al-lenguaje-de-programacion-go-golang) 3. [Estructuras de control de flujo en Go](https://steemit.com/cervantes/@orlmicron/estructuras-de-control-de-flujo-en-go) 4. [Array y Slice en Go (Golang)](https://steemit.com/cervantes/@orlmicron/array-y-slice-en-go-golang) 5. [Maps en Go (Golang)](https://steemit.com/cervantes/@orlmicron/maps-en-go-golang) 6. [Punteros en Go (Golang)](https://steemit.com/cervantes/@orlmicron/punteros-en-go-golang) 7. [Importaciones y paquetes en Go (Golang)](https://steemit.com/cervantes/@orlmicron/importaciones-y-paquetes-en-go-golang) 8. [Paquetes de terceros y alias en Go (Golang)](https://steemit.com/cervantes/@orlmicron/paquetes-de-terceros-y-alias-en-go-golang) 9. [Tipos de datos Personalizados y Métodos en Go (Golang)](https://steemit.com/cervantes/@orlmicron/tipos-de-datos-personalizados-y-metodos-en-go-golang) 10. [Estructuras: ¿Las Clases de Go (Golang)?](https://steemit.com/cervantes/@orlmicron/estructuras-las-clases-de-go-golang) 11. [Interfaces en Go (Golang)](https://steemit.com/cervantes/@orlmicron/interfaces-en-go-golang) 12. [Interfaces vacías en Go (Golang)](https://steemit.com/cervantes/@orlmicron/interfaces-vacias-en-go-golang) 13. [Manejo de errores en Go (Golang)](https://steemit.com/cervantes/@orlmicron/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](https://discordapp.com/invite/8cFAFTZ) dónde compartimos contenido de esa naturaleza y totalmente en español. Hasta la próxima. 