Go y Golang

Explicación de defer en Go

En este post se explica cómo usar defer en Go, así como sus ventajas e importancia.

El lenguaje de programación Go provee una característica muy especial y útil a la vez: defer. Esta sentencia ayuda mucho a la hora de programar con Go, pues permite ejecutar alguna sentencia al finalizar o terminar el flujo de una función, sin importar en qué punto se regrese.

Explicación y ventajas de defer en Go

Al principio suena complicada, pero su uso es realmente sencillo y no estamos obligados a usarlo si no queremos. De hecho se podría decir que es azúcar sintáctica del lenguaje.

Sentencia defer en Go

Esta sentencia ejecuta el código que le indicamos antes de que se termine la función, sin importar en qué punto se termine la misma. Un ejemplo básico es el siguiente (tomado de aquí):

package main

import "fmt"

func main() {
 defer fmt.Println("Mundo")
 fmt.Println("Hola")
}

Ese código imprimirá:

Hola

Mundo

En la primera línea se pone a defer fmt.Println("Mundo") y en la segunda fmt.Println("Hola"). Aunque parece que se imprimirá Hola y luego Mundo, el resultado es lo contrario.

Dicho de otra manera, el código de arriba es lo equivalente a escribir esto:

package main

import "fmt"

func main() {
 fmt.Println("Hola")
 fmt.Println("Mundo")
}

La sentencia defer en Go mueve el código al final de la función, si se quiere ver así, aunque en realidad lo ejecuta antes de que la función se termine, sin importar el punto en donde se hace el return.

Otro ejemplo de defer en Go

Veamos este programa que si bien no tiene mucho sentido, ejemplifica el uso de defer. Ya lo dije, defer permite que se ejecute código al terminar la función, sin importar en qué punto se termine.

package main

import "fmt"

func main() {
 fmt.Println("Escribe tu edad:")
 var edad int
 fmt.Scanln(&edad)

 // Al final, siempre mostrar este mensaje 
 defer fmt.Println("Gracias. Vuelva pronto")

 if edad < 18{
  fmt.Println("No puedes acceder al mensaje secreto")
  return//Termina la función
 }
 fmt.Println("Hola, soy un mensaje que se muestra si tienes más de 18 años")
}

Leemos a través del teclado la edad del usuario, si su edad es menor a 18, le indicamos que no puede ver el contenido y terminamos el flujo de la función con return.

En caso de que tenga más de 18 años, le mostramos un mensaje secreto.

Lo que quiero resaltar aquí, es que sin importar lo que el usuario escriba, al final se muestra el mensaje que dice que vuelva pronto. No importa si la función termina normalmente o “se interrumpe”, todo lo que esté agendado con defer se ejecutará antes de que la función se termine.

Equivalente en otros lenguajes

La sentencia defer en Go es un poco parecida a la sentencia finally de un try/catch que otros lenguajes de programación tienen; con la gran diferencia de que en Go no hay excepciones, pero esa es otra historia.

Recordemos que finally se ejecuta sin importar si se cae dentro del catch.

Ventajas de defer en Go

Vamos a analizar las ventajas que tiene defer en Go, porque esta sentencia, que yo sepa, no existe en los lenguajes de programación más populares.

Permite tener un código más legible

Cuando usamos defer, podemos terminar algo que estamos iniciando, pocas líneas después de lo primero. Por ejemplo, tomemos el ejemplo real en donde conectamos a Go con MySQL. Un fragmento de código para abrir la base de datos dice así:

db, err := obtenerBaseDeDatos()
if err != nil {
 fmt.Printf("Error obteniendo base de datos: %v", err)
 return
}
// Terminar conexión al terminar función
defer db.Close()

// Aquí 10 millones de líneas...

Como ya abrimos la base de datos, indicamos que antes de salir de la función actual, la misma se cierre con db.Close(). No importa si abajo hay 10 millones de líneas, la base de datos se cerrará cuando se termine.

En un futuro cuando nosotros u otro programador lea el código, sabrá que la base de datos que se está abriendo, se está cerrando al final; sin importar en dónde.

Además, si más abajo hay múltiples condiciones que terminan la función, no tenemos que ir dentro de cada una de ellas y poner db.Close() en caso de que la función no siga su flujo normal.

Evita olvidar cerrar archivos, bases de datos, etcétera

El mismo ejemplo de arriba. Si hubiera muchas líneas de código y no existiera defer, a menudo olvidaríamos terminar lo que empezamos.

El ejemplo fue con una base de datos, pero lo mismo pasa con un archivo y cualquier otra cosa que se deba terminar cada que se empieza.

Más cosas de defer

Solamente queda mencionar que se puede llamar a defer las veces que sea necesario. Cuando eso pase, las sentencias se ejecutan en el orden inverso de como fueron llamadas.

Un ejemplo sencillo es el siguiente, en donde imprimimos 4 mensajes, 3 de ellos con defer. Al final, los mensajes se imprimirán ordenados.

package main

import "fmt"

func main() {
 // Normal
 fmt.Println("Hola, mundo")

 // Se ejecutarán al revés
 defer fmt.Println("parzibyte.me")
 defer fmt.Println("defer desde")
 defer fmt.Println("probando")
}

Esto es porque la primera llamada a defer se ejecuta al final, la segunda antes del final, y así sucesivamente. El orden es LIFO: último en entrar, primero en salir.

Así es como funciona defer en Go.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Muy buena entrega, solo algo en tu primer ejemplo en el de Hola Mundo, lo tienes cambiado, para que te quede perfecto 👌

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

3 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

3 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

3 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

3 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

3 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

4 días hace

Esta web usa cookies.