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
.
Publicaciones relacionadas
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.
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!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit