Go es un lenguaje de programación desarrollado por Google pensado para la simplicidad y la eficiencia siendo confiable, con una velocidad de respuesta semejante a lenguajes como C o C++, logrando acogida para la gestión de concurrencia en la Web. Además de estar disponible en varios sistemas (para Windows, macOS, Linux), posibilita la programación para la web con WebAssembly. También puede ser interesante usar Go como complemento con otros lenguajes (interoperabilidad), logrando así un tiempo de respuesta favorable.
Aunque es un lenguaje compilado, se pueden crear scripts para el sistema usando Go, no sólo para acceder al sistema de archivos, sino que también se puede conectar a bases de datos, y por qué no, con almacenamiento en la nube. Por esto es frecuente encontrarlo tanto en software de Backend como en DevOps.
Esta es una referencia ágil para quién tenga nociones de programación o codifique con algún otro lenguaje, también a modo de repaso y uso frecuente. Siendo así, tener esta información como memoria te resultará simple de acceder a los fundamentos, incluso como prueba de concepto sobre el lenguaje.
Para quien tenga habilidades, experticia en programación y/o requiera agilidad en conceptos técnicos, pueden resumirse los siguientes tips esenciales del lenguaje:
int
, uint
, float32
, byte
, bool
, string
, struct
. Las variables se definen con var
, el nombre y el tipo de datos. Se puede asignar el valor directamente sin necesidad de especificar el tipo de datos usando el operador :=
.func
, luego los parámetros van entre paréntesis (...)
continuando con el tipo de datos del parámetro (y separando los parámetros con coma ,
). El tipo de datos a retornar va después del cierre del paréntesis. Además, se usa return
para retornar un valor. La función principal de un programa se denomina main
.{}
. Las sentencias terminan con la línea sin el uso de punto y coma.C
, Java
, Javascript
, Kotlin
, es decir que se cuenta con una anatomía cercana para el uso de if
, for
, while
pero sin el uso de paréntesis y no existe try ... exception
(pero tiene otro modo de controlar errores).struct
y funciones asociadas).Para información detallada puede consultarse: https://github.com/jltabara/Libro-Golang/blob/master/LibroV1.md
package main
import (
"fmt"
)
func main() {
fmt.Println("Hi there!")
}
En un programa inicial, siempre encuentras
package
,import
y el bloque parafunc main()
. Revisa la instalación del lenguaje y el proyecto de inicio rápido avanzando el documento
Si tienes conocimiento en Javascript
u otro lenguaje, puede ser útil un sencillo escenario de comparación que nos da una referencia inmediata de Go
.
Concepto | Javascript | Go |
---|---|---|
Variable | let variable = ‘Ana’ | variable := “Ana” |
Función | function () { } | func main() { … |
Condición | if (i == 1) { } | if i == 1 { … |
Ciclo For | for (let i = 0; i < 10; i++) { } | for i := 0; i < 10; i++ { … |
Excepción | try { } catch(err) { } | if err != nil { … |
En
Go
no existen excepciones propiamente pero se pueden gestionar los errores usando una variable y una condición como se verá avanzando el texto.
Se puede declarar variables con la palabra reservada var
y el tipo de datos porterior al nombre, o simplemente usando la declaracion corta que asigna un valor con el operador :=
. Si se declara a nivel global, suele usarse var ( ... )
, aunque en ese caso debe revisarse si se trata de constantes usando const ( ... )
.
Ejemplo:
name := "Ana"
var n int
var a [10]int
v := [3]int{1,2,3}
En el caso de arreglos (últimas dos líneas del ejemplo), los elementos deben corresponder a un solo tipo de datos y si se usa el operador
:=
se asignan los valores iniciales usando llaves{}
.
func sum(a int, b int) int {
return a + b
}
Basicamente se usa la palabra servada
func
. Si devuelve un valor se indica el tipo de dato y se usa internamente la palabra reservadareturn
. Veamos un ejemplo.
if i == 1 {
fmt.Println("one")
} else if i == 2 {
fmt.Println!("two")
} else {
fmt.Println!("aha")
}
Nótese que se usa,
if ... {
,} else if ... {
o} else {
, es decir, en tanto no existe caracter indicador de fin de línea (;
en otros lenguajes), el lenguaje se expresa de modo estricto con estas expresiones sin dar lugar a variación en la organización del código. Así mismo, una condición simple siempre será expresada entre llaves{}
, puesto que se maneja el concepto de bloques de código y el alcance de variables entre dichas llaves, del mismo modo que en las funciones.
for i := 0; i < 5; i++ {
fmt.Println(i)
}
En
Go
sólo existe el ciclofor
. Nótese que se usa;
para separar las partes de inicialización, condición para terminar y el incremento. Además, se usa:=
para establecer la variable con un valor inicial.
También podemos expresarlo de modo simple, es decir, aplicando sólo la condición (semejante a while
en otros lenguajes), pero se debe controlar la inicialización y el incremento de la iteración. Veamos:
i := 0
for i < 5 {
i++
fmt.Println(i)
}
fmt.Println("Programa terminado")
Dado que en Go
sólo existe el ciclo for
, para recorrer un arreglo puede usarse la palabra reservada range
que devuelve dos valores y el ciclo termina automaticametne al finalizar el rango. Por ejemplo:
a := []string{"a","b","c"}
for index, value := range a {
fmt.Println(index, value)
}
Lo mismo ocurre cuando se trata de mapas o tuplas (que se inicializan con la palabra reservada make
). Por ejemplo:
m := make(map[string]string)
m["a"] = "apple"
m["b"] = "banana"
for index, value := range m {
fmt.Println(index, value)
}
En Go
no aplica el concepto de excepciones de otros lenguajes. Básicamente, si una función puede producir un error se devuelven dos valores, uno para el resultado natural que retorna la función y otro para el error, y se debe validar el resultado de la variable de error usada (if err != nil {...
). Veamos un ejemplo:
package main
import (
"fmt"
"strconv"
)
func main() {
s := "5"
n, err := strconv.Atoi(s)
if err != nil {
fmt.Println("Error:", err)
return
}
n = n + 10
fmt.Println(n)
}
En este caso se usa el paquete
strconv
para intentar convertir una cadena a número y luego incremtarlo en 10 unidades, si la variableerr
es diferente denil
(nulo) entonces se identifica un error en la conversion.
Como en lenguaje C
y C++
, el concepto de punteros se puede entender como variables que se pasan como referencia, es decir, en tanto se usan como parámetro en otra función se apunta a la misma posición de memoria y por tanto se altera el resultado de la variable original. En últimas, lo que se espera es reflejar el cambio de un valor en otra parte del programa, sólo que en Go
se simplifican aspectos. Veamos un ejemplo:
package main
import (
"fmt"
)
func main() {
i := 5
increment(&i)
fmt.Println(i)
}
func increment(x *int) {
*x++
}
Se usa el signo
&
en la invocacion para denotar la referencia y el signo*
en la funcion (tanto en la declaración del tipo recibido como en el uso de la variable interna).
Con la palabra reservada struct
(combinada con type
) se definen estructuras de datos u objetos. Veamos un ejemplo.
package main
import (
"fmt"
)
type Person struct {
name string
lastname string
age int
}
func main() {
person := Person{name: "John", lastname: "Doe", age: 22}
fmt.Println(person)
}
También se puede iniciar vacío usando
new(Person)
. Además, se puede recorrer un arreglo de datos usando la variable y la funciónrange
. Por ejemplo:
func main() {
persons := []Person{name: "John", lastname: "Doe", age: 22}
for _,person := range persons {
fmt.Println(person)
}
}
En Go
no hay clases pero se puede homologar métodos que corresponderían a funciones que utilizan un identificador para un struct
. Veamos esto con la estructura anterior y un ejemplo:
func (this Person) fullname() string {
return this.name + " " + this.lastname
}
func main() {
p := Person{name: "John", lastname: "Doe", age: 22}
fmt.Println(p.fullname())
}
Nótese que antes del nombre de la función se establece un identficador que refiere el
struct
asociado a la función:(this Person)
. Este modo de implementación se aplica en cada método asociado a unstruct
. En el ejemplo se ha usadothis
como identificador pero puede tener otro nombre distinto, dado que no se trata de una palabra reservada del lenguaje. También se puede recibir elstruct
como puntero (agregando el*
como se ha visto antes).
Además, se pueden implementar interfaces usando en lugar de struct
la palabra reservada interface
que se trata de otro type
. Dentro de estas se reportan las funciones.
Veamos un ejemplo:
package main
import (
"fmt"
"strconv"
)
type UserInput interface {
Add(rune)
GetValue() string
}
type NumericInput struct {
input string
}
func (this NumericInput) Add(rune string) {
n, err := strconv.ParseFloat(rune, 64)
if err != nill {
this.input += rune
}
}
func (this NumericInput) GetValue() string {
return this.input
}
func main() {
var input UserInput = &NumericInput{}
input.Add('1')
input.Add('a')
input.Add('0')
fmt.Println(input.GetValue())
}
package main
import (
"fmt"
"time"
)
func ahaGo(index int) {
fmt.Println(index)
}
func tryGo(n int){
for i := 0; i < n; i++ {
go ahaGo(i)
}
}
func main(){
go tryGo(20)
time.Sleep(2000 * time.Millisecond)
}
Con la palabra reservada
go
se define unaGoroutine
liberando la lógica de una secuencia estricta pero atendiendo ágilmente la función correspondiente (en este caso, imprimir un número). Este programa puede arrojar un resultado como:
0
1
6
2
3
4
5
9
7
8
11
10
12
13
14
15
19
16
17
18
Veamos otro ejemplo:
package main
import (
"fmt"
"math"
)
func weeks(days int) int {
var result float64
n, _ := math.Modf(days / 7)
return int(n)
}
func main() {
m := map[string]int{ "Robert" : 30, "John" : 475, "Elly" : 1022, "Bob" : 99 }
go func() {
for p, days := range m {
w := weeks(days)
fmt.Println(p, " ha trabajado en la empresa ", w, " semanas")
}
}()
}
Dada la simplicidad de Go
podemos citar tan solo 25 palabras reservadas en sus primeras versiones.
. | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | break | default | func | interface | select |
2 | case | defer | go | map | struct |
3 | chan | else | goto | package | switch |
4 | const | fallthrough | if | range | type |
5 | continue | for | import | return | var |
Para instalar Go
te diriges al sitio oficial y descargas el instalador (en Linux puede usarse el comando apt-get
), luego se ejecuta el archivo descargado.
El instalador sugiere que se instale en la carpeta C:\Go
que correspondería a la variable de entorno GOROOT
(raíz del lenguaje que contiene la subcarpeta bin
). Para la variable GOPATH
bajo Windows, automaticamente asigna el valor %USERPROFILE%/go
(dónde %USERPROFILE%
es la carpeta de inicio del usuario que lo instala), y apunta a las carpetas src
, pkg
y bin
. Si se cambia la ruta de instalacion de Go, es preciso reportar la variable de entorno GOROOT
con la nueva ruta correspondiente.
Considere verificar que las variables de entorno
GOROOT
yGOPATH
se encuentren debidamente configuradas y agregarlas enPATH
, por ejemplo:PATH=%GOROOT%\bin;%GOPATH%
También puede usarse scoop
en Windows (si esta previamente instalado) usando PowerShell con la sentencia:
scoop install go
Para Chocolatey se usa:
choco install golang
Si el sistema que utilizas para desarrollar es Linux Ubuntu/Debian, puedes ejecutar la siguiente instrucción:
sudo apt install -y golang
Para Linux Enterprise como CentOS use:
sudo yum install -y golang
Para macOS se usa Homebrew ejecutando:brew install go
Una opción para instalar la versión mas reciente Go es usar el paquete para Linux Ubuntu. Basta con descargar desdel el sitio oficial el archivo con extensión .tar.gz
. Veamos un ejemplo usando los comandos necesarios para instalar la versión mas reciente en Linux.
sudo rm -rf /usr/lib/go-*
wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz
tar -xvzf go1.22.1.linux-amd64.tar.gz
sudo mv go /usr/lib/go-1.22.1
cd /usr/lib
sudo rm -rf go
sudo ln -s go-1.22.1 go
cd ../bin
sudo rm -rf go gofmt
sudo ln -s ../lib/go-1.22.1/bin/go go
sudo ln -s ../lib/go-1.22.1/bin/gofmt gofmt
go version
cd ../share
sudo rm -rf go
sudo mkdir go
Puedes ejecutar estos comandos desde una terminal uno por uno. Incluso, se pude haber instalado antes la version por defecto de Go para Ubuntu y luego usar estos comandos para dejar la versión reciente funcionando.
Para organizar nuestros paquetes modularmente podemos usar una sentencia como la siguiente:
go mod init mymodule
mymodule
se refiere al nombre asginado a nuestro módulo
Para instalar módulos de librerías se usa go get ...
(indicando la librería o url
), por ejemplo, para usar el controlador de MongoDB
ejecutas:
go get go.mongodb.org/mongo-driver/mongo
Creamos una carpeta nueva en una ubicación destinada para el desarrollo de nuestro proyecto e incluimos un archivo con extension .go
(por ejemplo, server.go
). Esto puede hacerse desde un editor como Visual Studio Code (VSCode). Podemos incluir el siguiente código.
package main
import (
"fmt"
"log"
"net/http"
)
const (
HOST = "localhost"
PORT = "8080"
)
func start(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "It works!")
}
func main() {
fmt.Println("Listen on", HOST, "[", PORT, "]")
http.HandleFunc("/", start)
err := http.ListenAndServe(HOST+":"+PORT, nil)
if err != nil {
log.Fatal("Error listening server: ", err)
return
}
}
Este programa deja a disposición un servidor lógico para la Web que muestra un mensaje en el navegador. Para compilar el programa y ejecutarlo se procede de la siguiente manera:
go build server.go
server.exe
Dado que se trata de un servidor web, usamos un navegador para consultar la ruta respectiva (localhost:8080
) y probar el programa.
Veamos ahora como lograr un servidor web en simples pasos con la librería Fiber
. Primero descargamos la librería.
go get github.com/gofiber/fiber
Abrimos el editor y nombramos nuestro programa, por ejemplo server.go
, y colocamos el siguiente contenido:
package main
import "fmt"
import "github.com/gofiber/fiber"
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hi there!")
})
app.Listen(":8000")
}
Ejecutamos el programa con el comando:
go run server.go
Puedes revisar en un navegador consultando la dirección:
localhost:8000
Podemos embeber algunos lenguajes de “script” en Go. Tal es el caso del proyecto goja que permite ejecutar Javascript. Veamos un ejemplo:
package gojs
import (
"fmt"
"github.com/dop251/goja"
"github.com/dop251/goja_nodejs/console"
"github.com/dop251/goja_nodejs/require"
)
func plainJS() {
vm := goja.New()
new(require.Registry).Enable(vm)
console.Enable(vm)
script := `
console.log("Hello world - from Javascript inside Go! ")
`
fmt.Println("Compiling ... ")
runnable, err := goja.Compile("", script, true)
if err != nil {
fmt.Printf("Error compiling the script %v ", err)
return
}
fmt.Println("Running ... \n ")
_, err = vm.RunProgram(runnable)
}
func main() {
plainJS()
}
Veamos a continuación un ejemplo de código con la estructura orientada para AWS Lambda. Pero antes de ello definimos nuestro proyecto ubicándonos en una carpeta usado como espacio de trabajo en dónde estableceremos la carpeta para nuestro proyecto (con un archivo main.go
) y lo iniciaremos con las siguientes sentencias:
mkdir golambda
cd golambda
go mod init golambda
go get -u github.com/aws/aws-lambda-go
vim main.go
golambda
corresponde al nombre de nuestro proyecto. Usamosgo mod init
para preparar el proyecto ygo get
para incorporar módulos (AWS).
La última sentencia representa la edición de un nuevo archivomain.go
, pero puede usarse un editor como VSCode en lugar devim
.
El código de ejemplo tendrá un contenido como el siguiente:
package main
import "log"
import "github.com/aws/aws-lambda-go/events"
import "github.com/aws/aws-lambda-go/lambda"
func main() {
lambda.Start(handler)
}
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
response := events.APIGatewayProxyResponse{
StatusCode: 200,
}
log.Println("Hi there!")
return response, nil
}
Antes de compilar, debemos garantizar un binario para Linux. Si tienes macOS se ejecuta
GOOS=linux
, para Windows se usa en PowerShell$Env:GOOS="linux"
.
Para probar la Lambda se compila congo build main.go
y se comprime el archivo binario obtenido, para luego subirlo manualmente a AWS Lambda (indicando que el Handler se denominamain
)