Conociendo Panic y Recover en Go (Golang)

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@orlmicron·
0.000 HBD
Conociendo Panic y Recover en Go (Golang)
![gopher wallpaper](https://cdn.steemitimages.com/DQmNTeZpCmtrUNE2WwCkUpbQgFrhz6bjQ4Evt9f5Rgp1n9m/gopher-wallpaper-02.jpg)

# 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>![separator.png](https://cdn.steemitimages.com/DQmXF55A7pDHvVu4K73h6TgCoH5UpC1WUHFAFhn76NUVgUC/separator.png)</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.

![banner-steemit.jpg](https://cdn.steemitimages.com/DQmQU5fep6GDnVe1KD4jRgxwD6DcBNvwWPTx82DKBWeciew/banner-steemit.jpg)
👍 , , , , , , , , , ,