Todo lo que necesitas saber sobre Paquetes en Go

Inicialización de paquetes

Cuando ejecutamos un programa Go, el compilador Go sigue un determinado orden de ejecución para los paquetes, los ficheros de un paquete y la declaración de variables en esos ficheros.

Alcance de paquete

Un alcance es una región en un bloque de código donde una variable definida es accesible. Un ámbito de paquete es una región dentro de un paquete donde una variable declarada es accesible desde dentro de un paquete (a través de todos los archivos del paquete). Esta región es el bloque superior de cualquier archivo del paquete.

Mira el comando go run. Esta vez, en lugar de ejecutar un archivo, tenemos un patrón glob para incluir todos los archivos dentro del paquete app para su ejecución.

Go es lo suficientemente inteligente como para averiguar un punto de entrada de la aplicación que es entry.go porque tiene la función main. También podemos usar un comando como el siguiente (el orden del nombre del archivo no importa).

go run src/app/version.go src/app/entry.go

💡 También se puede usar el comando go run app que hace lo mismo y este es un mejor enfoque para ejecutar un paquete. El comando go install o go build requiere un nombre de paquete, que incluye todos los archivos dentro de un paquete, por lo que no tenemos que especificarlos como arriba.

Volviendo a nuestro tema principal, podemos usar la variable version declarada en el archivo version.go desde cualquier parte del paquete aunque no esté exportada (como Version), porque está declarada en el ámbito del paquete.

Si la variable version se hubiera declarado dentro de una función, no estaría en el ámbito del paquete y el programa anterior habría fallado al compilar.

No se permite volver a declarar una variable global con el mismo nombre en el mismo paquete. Por lo tanto, una vez que la variable version se declara, no se puede volver a declarar en el ámbito del paquete. Pero usted es libre de volver a declarar en otro lugar.

Inicialización de variables

Cuando una variable a depende de otra variable b, b debe ser definida de antemano, de lo contrario el programa no compilará. Go sigue esta regla dentro de las funciones.

Pero cuando estas variables se definen en el ámbito del paquete, se declaran en ciclos de inicialización. Veamos el siguiente ejemplo sencillo.

En el ejemplo anterior, primero se declara c porque su valor ya está declarado. En el ciclo de inicialización posterior, se declara b, porque depende de c y el valor de c ya está declarado. En el ciclo de inicialización final, a es declarado y asignado al valor de b. Go puede manejar ciclos de inicialización complejos como el siguiente.

En el ejemplo anterior, primero se declarará c y luego b ya que su valor depende de c y finalmente a ya que su valor depende de b. Debe evitar cualquier bucle de inicialización como el de abajo, donde la inicialización entra en un bucle recursivo.

Otro ejemplo de ámbito del paquete sería, tener una función f en un archivo separado que hace referencia a la variable c del archivo principal.

Función de inicio

Como la función main, init la función es llamada por Go cuando un paquete es inicializado. No toma ningún argumento y no devuelve ningún valor.

La función init es declarada implícitamente por Go, por lo que no se puede referenciar desde ningún sitio (o llamarla como init()). Se pueden tener varias funciones init en un fichero o paquete. El orden de ejecución de las funciones init en un fichero será según el orden de aparición de las mismas.

Puedes tener funciones init en cualquier parte del paquete. Estas funciones init se llaman en orden léxico de nombre de archivo (orden alfabético).

Después de que todas las funciones init se ejecuten, se llama a la función main. Por lo tanto, el trabajo principal de la función init es inicializar las variables globales que no pueden ser inicializadas en el contexto global. Por ejemplo, la inicialización de un array.

Dado que, la sintaxis for no es válida en el ámbito del paquete, podemos inicializar el array integers de tamaño 10 utilizando el bucle for dentro de la función init.

Alias de paquetes

Cuando se importa un paquete, se crea una variable utilizando la declaración del paquete. Si usted está importando varios paquetes con el mismo nombre, esto dará lugar a un conflicto.

// greet/parent.go
package greetvar Message = "Hey there. I am parent."// greet/greet/child.go
package greetvar Message = "Hey there. I am child."

Por lo tanto, utilizamos alias de paquete. Declaramos un nombre de variable entre la palabra clave import y el nombre del paquete que se convierte en la nueva variable para hacer referencia al paquete.

En el ejemplo anterior, greet/greet paquete ahora se hace referencia por child variable. Si te das cuenta, aliased greet paquete con un guión bajo.

Underscore es un carácter especial en Go que actúa como null contenedor. Como estamos importando el paquete greet pero no lo usamos, el compilador Go se quejará de ello. Para evitar eso, estamos almacenando la referencia de ese paquete en _ y el compilador Go simplemente lo ignorará.

Aliasing un paquete con un guión bajo que parece no hacer nada es bastante útil a veces cuando se quiere inicializar un paquete pero no usarlo.

// greet/parent.go
package greetimport "fmt"var Message = "Hey there. I am parent."func init() {
fmt.Println("greet/parent.go ==> init()")
}// greet/greet/child.go
package greetimport "fmt"var Message = "Hey there. I am child."func init() {
fmt.Println("greet/greet/child.go ==> init()")
}

Lo principal a recordar es que un paquete importado se inicializa sólo una vez por paquete. Por lo tanto, si usted tiene múltiples declaraciones de importación en un paquete, un paquete importado va a ser inicializado sólo una vez en la vida de la ejecución del paquete principal.