You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1784 lines
49 KiB

---
weight: 4
title: "Apuntes del lenguaje de programación Go"
date: 2021-05-18T09:55:41+0200
draft: false
summary: "Apuntes de Go"
categories:
- notes
tags:
- golang
- programacion
---
{{< admonition type=warning title="Work in progress" open=true >}}
Apuntes incompletos del lenguaje de programación Go
{{< /admonition >}}
## Referencias
- [Documentación Oficial](https://godoc.org/)
- [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments)
- [Golang 101 hacks](https://nanxiao.gitbooks.io/golang-101-hacks/content/)
- [Un video curso de Traversy Media](http://www.tonvid.com/info.php?video_id=SqrbIlUwR0U)
- [Un video curso de 7 horas de freeCodeCamp](http://www.tonvid.com/info.php?video_id=YS4e4q9oBaU)
- [Go Workshop Book](https://subscription.packtpub.com/book/ppprogramming/9781838647940)
- [Awesome Go](https://awesome-go.com/)
- [Golang by example: All design patterns in Go](https://golangbyexample.com/all-design-patterns-golang/)
- [Design Patters](https://refactoring.guru/design-patterns/go) (lo mismo pero con más dibujitos)
- [Go Modules](https://go.dev/blog/using-go-modules)
- [Build a web app in Golang](https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/01.1.html)
### Books
- Esencia del lenguaje Go: ["The Go Programming Language" Addison-Wesley](https://www.amazon.es/dp/0134190440/ref=cm_sw_r_cp_apa_fabc_7-7WFbSZRP6WG)
- Panorámica del go-stdlib: ["Mastering Go" Packt Publishing; 2nd Revised edition](https://www.amazon.es/dp/1838559337/ref=cm_sw_r_u_apa_i_DB8VXX0DNT6NPRMRAMHJ)
## Instalación
- Bajamos el paquete con la última versión desde la [página oficial](https://go.dev/dl/).
- Y lo descomprimimos como root en `/usr/local/`
```bash
cd ~/tmp/go
wget https://go.dev/dl/go1.17.5.linux-amd64.tar.gz
sudo cp /usr/local/go /usr/local/go.bkp
sudo tar -C /usr/local -xvzf go1.17.4.linux-amd64.tar.gz
```
Después de instalar es importante ajustar las rutas en `.profile` (ver la explicación en el siguiente punto):
```bash
# golang
if [ -d "/usr/local/go" ] ; then
# export GOROOT="/usr/local/go"
# PATH="$PATH:$GOROOT/bin"
fi
if [ -d "$HOME/go" ] ; then
export GOPATH="$HOME/go"
PATH="$PATH:$GOPATH/bin"
fi
export PATH
```
{{< admonition type=danger title="GOROOT" open=true >}}
Varios autores ([un ejemplo](https://dave.cheney.net/2013/06/14/you-dont-need-to-set-goroot-really)) recomiendan **NO** establecer el valor de `GOROOT` cuando usamos la localización estándar. Así que he comentado esa parte en mi fichero `~/.profile`
{{< /admonition >}}
### Configuración del entorno de Go
{{< admonition type=info title="Referencias" open=false >}}
- [Golang Doc: gopath_code](https://golang.org/doc/gopath_code)
- [Golang Enviroment Configuration](https://www.metaltoad.com/blog/golang-environment-configuration)
- [Build a Web Application with Golang](https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/01.0.html)
{{< /admonition >}}
Para dejar configurado el entorno de trabajo en _Go_ conviene conocer las siguientes variables de entorno (aunque hay [más](https://golang.org/doc/)):
**GOROOT**
: Es el directorio donde se localiza la biblioteca estándar de _Go_. Por defecto _Go_ asume la ruta `/usr/local/go`, así que estrictamente hablando **NO** es necesario establecer esta variable si has instalado en `/usr/local`. (Con [retoques de esta variable](https://golang.org/doc/manage-install) es posible instalar varias versiones de _Go_)
**GOBIN**
: Es el directorio donde quedan instalados los binarios de _Go_ cuando ejecutamos un comando `go install ...`. Solo deberías tocar esta variable si no quieres instalar los binarios en la localización por defecto: `$GOPATH/bin`.
**GOOS**
: especifica el sistema operativo, es opcional. En mi caso tiene valor `linux`
**GOARCH**
: especifica la arquitectura del procesador de nuestra máquina, también es opcional, en mi caso es `amd64`
**GOPATH**
: La ruta a nuestro directorio de trabajo (nuestro _workspace_). Esta variable es **obligatoria** y **no tiene valor por defecto**, podeis ponerla en donde tenga sentido para vosotros, por ejemplo `/home/salvari/code/go`
Evidentemente tenemos que dejar las variables de entorno exportadas y añadir al `PATH` de nuestro usuario las que correspondan (ver punto anterior)
{{< admonition type=tip title="Ver el entorno" open=true >}}
Puedes ver el entorno completo de Go con el comando `go env`
{{< /admonition >}}
#### Más de GOPATH
{{< admonition type=warning title="Abandonando GOPATH" open=true >}}
Estamos usando ya go 1.17. Hay planes firmes para abandonar el tratamiento de dependencias via `GOPATH` y centrarse exclusivamente en gestionarlas a través de módulos.
Todo lo que comento aquí de `GOPATH` hay que tratarlo con pinzas.
{{< /admonition >}}
_Go_ espera que en el directorio `$GOPATH` haya tres subdirectorios:
```bash
bin/
pkg/
src/
```
- `bin/` contiene los ejecutables que se generan cuando ejecutamos `go install ...`
- `pkg/` contiene los paquetes instalados en nuestro sistema con `go get ...`
- `src/` **aquí es donde deberíamos alojar nuestro código fuente** (no es obligatorio)
Como hemos dicho en `$GOPATH/src/` es donde tenemos que programar, ahí es donde tiene que estar nuestro código fuente, pero ojo la ruta exacta de nuestros proyectos es función de nuestra plataforma de control de código, y tiene que tener la forma `Source-Control-Platform/User/Repository`.
Por ejemplo podríamos tener los siguientes proyectos en el directorio `src/`:
```bash
src/github.com/salvari/miAppGo
src/gilab.com/salvari/mach5
```
### Instalación de herramientas
#### _golint_ y _godoc_
**Ya no** instalamos con los comandos:
```bash
go get -u golang.org/x/lint/golint
go get -u golang.org/x/tools/cmd/godoc
```
El comando `go get` se usa exclusivamente para añadir dependencias al módulo que estemos creando.
Los binarios que queremos usar en nuestro sistema se instalan con el nuevo estilo de instalación [recomendado](https://go.dev/doc/go-get-install-deprecation) desde go v1.17:
```bash
go install golang.org/x/lint/golint@latest
go install golang.org/x/tools/cmd/godoc@latest
```
Alternativamente podemos instalar **todas** las herramientas de _Go_ con:
```bash
go get -u golang.org/x/tools/...
```
Pero con esta opción me falla la instalación de `gopls`, una herramienta que necesito en mi configuración de _Emacs_
#### _gopls_ para protocolos LSP en editores
Desde un directorio **que no sea el GOPATH**
```bash
# GO111MODULE=on # not needed with 'go install'
go install golang.org/x/tools/gopls@latest
```
#### Herramientas adicionales
- gopkgs
- go-outline
- dlv
- dlv-dap
- staticcheck
```bash
go install github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest
go install github.com/ramya-rao-a/go-outline@latest
go install github.com/go-delve/delve/cmd/dlv@latest
go install github.com/go-delve/delve/cmd/dlv@master
go install honnef.co/go/tools/cmd/staticcheck@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
```
### Alias para zsh
```zsh
# golang aliases
alias gob='go build' # Build your code
alias goc='go clean' # Removes object files from package source directories
alias god='go doc' # Prints documentation comments
alias gof='go fmt' # Gofmt formats (aligns and indents) Go programs.
alias gofa='go fmt ./...' # Run go fmt for all packages in current directory, recursively
alias gog='go get' # Downloads packages and then installs them to $GOPATH
alias goi='go install' # Compiles and installs packages to $GOPATH
alias gol='go list' # Lists Go packages
alias gom='go mod' # Access to operations on modules
alias gop='cd $GOPATH' # Takes you to $GOPATH
alias gopb='cd $GOPATH/bin' # Takes you to $GOPATH/bin
alias gops='cd $GOPATH/src' # Takes you to $GOPATH/src
alias gor='go run' # Compiles and runs your code
alias got='go test' # Runs tests
alias gov='go vet' # Vet examines Go source code and reports suspicious constructs
```
## Go Modules
Básicamente un módulo (_Go Module_) es una colección de paquetes (_packages_) almacenados en un arbol de directorios que tiene un fichero `go.mod` en su raiz. El fichero `go.mod` especifica el _module path_ que es la ruta canónica al módulo que se usará para importarlo, y todas las depencias requeridas por el módulo, es decir el conjunto de módulos (otros módulos) que serán necesarios para compilar con éxito este módulo. Cada dependencia se especifica con su correspondiente _module path_ y un número de versión semántica (_semantic version_)
### Crear un módulo
El comando para crear un módulo es: `go mod init`
Antiguamente el compilador distinguía dos formas de funcionamiento, el _go module mode_ y el _gopath mode_. A estas alturas tenemos que usar el _go module mode_ el otro está en desuso. De hecho el compilador ahora funciona en _go module mode_ por defecto.
Si estamos en un directorio por debajo del `$GOPATH` nuestro directorio de proyecto debería tener esta forma:
```bash
$GOPATH/src/example.com/$USER/moduleName
```
En este caso el comando `go mod init` no necesita parámetros, genera un _module path_ basado en la estructura de directorios.
Si estamos trabajando en un directorio fuera del `$GOPATH` tendremos que especificar el _module path_ en el comando:
```bash
go mod init example.com/modulename
```
### Actualizar el fichero `.mod`
Ejecutamos: `go mod tidy`
## TDD (_Test Drived Development_) en Go
{{< admonition type=info title="Referencias" open=true >}}
- [Learn Go with Tests](https://quii.gitbook.io/learn-go-with-tests/) Un buen sitio para empezar
- [An introduction to Testing in Go](https://tube.cthd.icu/watch?v=GlA57dHa5Rg) Un video muy ilustrativo
- [Advanced Testing in Go](https://tube.cthd.icu/watch?v=S1O0XI0scOM) Continuación del anterior
{{< /admonition >}}
TDD es una buena práctica:
- Mejora la calidad del código
- En general hace que escribamos un código más desacoplado (tiendes a aislar más las cosas para facilitar los test)
- Tiene la ventaja obvia de tener todo controlado con test fáciles de repetir
- Bien escritos valen como documentación de bajo nivel
- Previene regresiones (fallos que reaparecen al avanzar en el desarrollo)
- Potencia la arquitectura y diseño modulares
### Tres reglas ###
Se suele hablar del ciclo: ""rojo, verde y refactorizar" (_red, green and refactor_)
- Primero hay que escribir un test que falla
- Hay que programar lo justo para que el test no falle. No se puede escribir más código que el necesario para que el test no falle.
- Se revisa el código para refinarlo (tanto el código producto como el código de los test)
Los pasos que nos proponen en la página de _Learn Go with Test_:
- Escribe un test
- Haz lo necesario para que el compilador pase sin errores
- Ejecuta el test, comprueba que falla y que el mensaje de fallo es significativo
- Escribe el código suficiente para que el test no falle
- Refina el código (_Refactor_)
Nos dicen literalmente:
Este ciclo puede parecer tedioso pero es importante mantenerlo.
Al hacerlo no solo te aseguras de tener test significativos, sino que aseguras un buen diseño del software respaldado por la seguridad que dan los test.
Ver fallar el test es un paso importante por qué permite comprobar el mensaje de error (que debe ser significativo) Como desarrollador puede ser muy difícil trabajar con código cuando los mensajes de fallo de los test no dan una idea clara de que está pasando.
Asegurándote de que los test se ejecutan rápido y estableciendo un entorno de trabajo que facilite escribir los test puedes "entrar en sintonía" y programar de la forma más eficiente posible.
Si no escribes los test, te verás obligado a comprobar el funcionamiento del código ejecutándolo manualmente, eso rompera tu concentracion y a la larga te hara perder bastante mas tiempo que escribir correctamente los test.
### Sintáxis para test
Escribir un test es como escribir una función pero:
- Tiene que estar en un fichero de la forma `xxx_test.go`
- El nombre de la función que implementa el test tiene que empezar por `Test`
- La función que implementa el test tiene que tener un único argumento: `t *testing.T`
- Para poder usar ese tipo de argumento y otras facilidades de testeo es necesario hacer `import "testing"`
### Código independiente es más fácil de testear ###
En el ejemplo _Hello World_ es mejor escribir una funcion que devuelve la cadena con el saludo que una función que escribe el saludo. Por que así somos independientes de la salida que puede ser por pantalla, por una página web, por un mensaje, escrita, etc. etc. Y de paso es más simple de testear.
## Go
`_`
: Es el _black identifier_ podemos usarlo para pasar de variables que
no vamos a usar.
```golang
a = "Una cadena"
for _, r := range a {
fmt.Println(r)
}
```
`make`
: Parece que vale para hacer un _alloc_ de memoria.
```golang
counts := make(map[string]int)
```
`time`
: La especificación de formatos tiene su gracia ¬_¬ [ver referencia](https://golang.org/src/time/format.go)
### Arrays
```go
var myArray1 = [3]int // will be filled with so called
// zero values, for integers: 0
var myArray2 = [5]int{1,2,3,4,5} // number of values between { } can
// not be larger than size (ofc)
var myArray3 = []int{1,2,3,4} // the compiler will count the
// array elements for you
```
- [Diferencias entre array y slice](https://gist.github.com/josephspurrier/b5713f9a534afe3cfdd2)
- [The Geek Stuff: Golang Array vs Slice](https://www.thegeekstuff.com/2019/03/golang-slice-examples/)
- [A comprehensive guide on array and slices in Go](https://www.sohamkamani.com/golang/arrays-vs-slices/)
- [Go: Array vs Slices Bonanza](https://medium.com/@marty.stepien/arrays-vs-slices-bonanza-in-golang-fa8d32cd2b7c)
- [Slice Tricks by Ian Lance Taylor](https://github.com/golang/go/wiki/SliceTricks)
- [DigitalOcean: Understanding array and slices in Go](https://www.digitalocean.com/community/tutorials/understanding-arrays-and-slices-in-go)
## Go Cheat Sheet
Copia descarada de [esto](https://github.com/a8m/golang-cheat-sheet)
### Crédito
La mayor parte de lo que se cuenta en esta sección está copiado de [golang-cheat-sheet](https://github.com/a8m/golang-cheat-sheet)
Los créditos originales avisan de que la mayor parte de los ejemplos tomados de [A Tour of Go](http://tour.golang.org/), una excelente introducción al lenguage __Go__
### Go en resumen
* Lenguaje Imperativo
* Tipado estático
* Sintáxis muy similar a la de C (pero con menos paréntesis y sin punto y coma obligatorio) la estructura es parecida a la de Oberon-2
* Compila a código nativo (nada de vm)
* No hay clases, pero tiene _structs_ con métodos
* Interfaces
* No implementa herencia. Hay algo llamado [type embedding](http://golang.org/doc/effective%5Fgo.html#embedding)
* Las Funciones son ciudadanos de primera clase
* Las Funciones pueden devolver múltiples valores
* Tiene _closures_
* Hay Punteros pero sin aritmética de punteros
* La concurrencia es _"Built-in"_ mediante las primitivas: _Goroutines_ y _Channels_
### Sintáxis básica
#### Hello World
Fichero `hello.go`:
```go
package main
import "fmt"
func main() {
fmt.Println("Hello Go")
}
```
`$ go run hello.go`
### Operadores
#### Aritméticos
|Operador|Descripción|
|--------|-----------|
|`+`|addition|
|`-`|subtraction|
|`*`|multiplication|
|`/`|quotient|
|`%`|remainder|
|`&`|bitwise and|
|`\|`|bitwise or|
|`^`|bitwise xor|
|`&^`|bit clear (and not)|
|`<<`|left shift|
|`>>`|right shift|
#### Comparación
|Operador|Descripción|
|--------|-----------|
|`==`|equal|
|`!=`|not equal|
|`<`|less than|
|`<=`|less than or equal|
|`>`|greater than|
|`>=`|greater than or equal|
#### Lógicos
|Operador|Descripción|
|--------|-----------|
|`&&`|logical and|
|`\|\|`|logical or|
|`!`|logical not|
#### Otros
|Operador|Descripción|
|--------|-----------|
|`&`|address of / create pointer|
|`*`|dereference pointer|
|`<-`|send / receive operator (see 'Channels' below)|
### Declaraciones
__El tipo va después del indentificador de variable__
```go
var foo int // declaración sin inicialización
var foo int = 42 // declaración con inicialización
var foo, bar int = 42, 1302 // declaración e inicialización múltiples
var foo = 42 // se omite el tipo, será inferido
foo := 42 // abreviado, sólo es válido en el cuerpo de las funciones,
// el tipo siempre es implícito
const constant = "Esto es una constante"
// iota se puede usar para números que se incrementan, empezando por cero
const (
_ = iota
a
b
c = 1 << iota
d
)
fmt.Println(a, b) // 1 2 (0 is skipped)
fmt.Println(c, d) // 8 16 (2^3, 2^4)
```
### Funciones
```go
// una función simple
func functionName() {}
// función con parámetros (aquí también va el tipo después de la variable)
func functionName(param1 string, param2 int) {}
// multiples parámetros del mismo tipo
func functionName(param1, param2 int) {}
// podemos especificar el tipo devuelto por la función
func functionName() int {
return 42
}
// Pueden devolver multiples valores
func returnMulti() (int, string) {
return 42, "foobar"
}
var x, str = returnMulti()
// si los valores devueltos tienen nombre no hace falta especificarlos en el Return
func returnMulti2() (n int, s string) {
n = 42
s = "foobar"
// n and s will be returned
return
}
var x, str = returnMulti2()
```
#### Funciones como valores y _closures_
```go
func main() {
// asignar una función a una variable
add := func(a, b int) int {
return a + b
}
// usar el nombre para llamar a la función
fmt.Println(add(3, 4))
}
// 'Closures', lexically scoped: Las funciones pueden acceder valores que estában dentro del alcance (scope)
// cuando se definió la función
func scope() func() int{
outer_var := 2
foo := func() int { return outer_var}
return foo
}
func another_scope() func() int{
// won't compile because outer_var and foo not defined in this scope
outer_var = 444
return foo
}
// Closures
func outer() (func() int, int) {
outer_var := 2
inner := func() int {
outer_var += 99 // outer_var from outer scope is mutated.
return outer_var
}
inner()
return inner, outer_var // return inner func and mutated outer_var 101
}
```
#### Variadic Functions
```go
func main() {
fmt.Println(adder(1, 2, 3)) // 6
fmt.Println(adder(9, 9)) // 18
nums := []int{10, 20, 30}
fmt.Println(adder(nums...)) // 60
}
// Usando ... antes del nombre del tipo del último parámetro indicamos que la función acepta cero o mas parámetros de ese tipo
// La función se usa como cualquier otra, excepto que podemos pasar tantos parámetros como queramos del último parámetro definido
func adder(args ...int) int {
total := 0
for _, v := range args { // Iterates over the arguments whatever the number.
total += v
}
return total
}
```
### Tipos _Built-in_
```
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32 ~= a character (Unicode code point) - very Viking
float32 float64
complex64 complex128
```
### Conversión de tipos
```go
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// alternative syntax
i := 42
f := float64(i)
u := uint(f)
```
### Paquetes (_Packages_)
{{< admonition type=info title="Referencias" open=false >}}
* [Golangbot: Go Packages](https://golangbot.com/go-packages/)
* [The Go blog: Using Go Modules](https://blog.golang.org/using-go-modules)
* [How to write Go code with GOPATH](https://golang.org/doc/gopath_code)
{{< /admonition >}}
Las unidades de encapsulado en _Go_ de menor a mayor nivel serían:
- Funciones
- Paquetes
- Módulos
Los paquetes nos permiten organizar los ficheros de código para hacerlos modulares y reutilizables además de facilitar el mantenimiento del software.
* Cada fichero de código _Go_ debe pertenecer a un paquete. La pertenencia se declara con `package` al principio de cada uno de los ficheros fuente, evidentemente un paquete puede "poseer" varios ficheros.
* Los ejecutables están en el paquete `main`. Es un paquete especial.
* Por convención los programas ejecutables (los del paquete `main`) se llaman comandos (_commands_). El resto se llaman simplemente paquetes (_packages_)
* Por convención: <nombre_del_paquete> == último nombre del _import path_ (import path `math/rand` => package `rand`) Lo normal es que todos los ficheros del paquete `rand` se guarden en el directorio `rand/`
* Si el identificador empieza con mayúscula: se exporta el símbolo (será visible desde otros paquetes)
* Si el identificador empieza con minúscula: privado (no será visible desde otros paquetes)
Los **módulos** son coleciones de paquetes. Son imprescindibles para crear nuestros propios paquetes por qué la ruta de nuestros paquetes viene dada por el **módulo**. En cuanto quieras empezar a organizar tu código en paquetes inevitablemente tendrás que crear también **módulos**.
El flujo típico de trabajo para un proyecto estructurado con _Packages_ sería el siguiente:
- Supongamos que nuestro proyecto será un "pomodoro" y que va a gestionar _Timers_ y _Notifications_ (por decir algo)
- Creamos el directorio del proyecto que vamos a llamar `pomodoro`, dentro de ese directorio tendremos el _package main_ con los "comandos".
```bash
mkdir pomodoro
cd pomodoro
touch main.go
```
- Iniciamos el modulo en la raiz del proyecto
```bash
go mod init pomodoro
# Aunque lo mas correcto sería iniciarlo como
go mod init gitlab/salvari/pomodoro
```
- Creamos los subdirectorios para los _packages_: `timer` y `notification`. Dentro de los subdirectorios creamos un fichero de código (pueden ser tantos ficheros como queramos)
```bash
mkdir timer
touch timer/timer.go
mkdir notification
touch notification/notification.go
```
Tendremos una estructura de directorios:
```bash
pomodoro
├── go.mod
├── main.go
├── notification
│  └── notification.go
└── timer
└── timer.go
```
El contenido del fichero `go.mod` será (la versión de go depende de lo que tengas instalado):
```go
module pomodoro
go 1.17
```
Cada fichero con extensión `.go` debe empezar **siempre** con la declaración del _package_. En nuestro caso `main.go` declarará `package main` y por ejemplo `notification.go` declarará `package notification`.
Ya tenemos todo estructurado, ahora en nuestros ficheros de código podremos hacer _imports_ de este estilo
```go
import "pomodoro/timer"
import "pomodoro/notification"
```
#### Gestión de dependencias con módulos en Go
<https://go.dev/blog/go116-module-changes>
<https://stackoverflow.com/questions/53368187/go-modules-installing-go-tools/57317864#57317864>
### Control del flujo de programa
#### If
```go
func main() {
// Basic one
if x > 10 {
return x
} else if x == 10 {
return 10
} else {
return -x
}
// You can put one statement before the condition
if a := b + c; a < 42 {
return a
} else {
return a - 42
}
// Type assertion inside if
var val interface{}
val = "foo"
if str, ok := val.(string); ok {
fmt.Println(str)
}
}
```
#### Bucles
```go
// Solo hay bucle `for`, no hay `while`, ni `until`
for i := 1; i < 10; i++ {
}
for ; i < 10; { // bucle while
}
for i < 10 { // se pueden omitir los ; si solo hay una condición
}
for { // Si omitimos la condición tenemos un while (true)
}
// Podemos usar use break/continue en el bucle activo
// o usar break/continue con etiquetas (para bucles más externos)
here:
for i := 0; i < 2; i++ {
for j := i + 1; j < 3; j++ {
if i == 0 {
continue here
}
fmt.Println(j)
if j == 2 {
break
}
}
}
there:
for i := 0; i < 2; i++ {
for j := i + 1; j < 3; j++ {
if j == 1 {
continue
}
fmt.Println(j)
if j == 2 {
break there
}
}
}
```
#### Switch
```go
// switch statement
switch operatingSystem {
case "darwin":
fmt.Println("Mac OS Hipster")
// cases break automatically, no fallthrough by default
case "linux":
fmt.Println("Linux Geek")
default:
// Windows, BSD, ...
fmt.Println("Other")
}
// al igual que con el 'for' y el 'if' podemos tener una sentencia de asignación justo antes de la variable del switch
switch os := runtime.GOOS; os {
case "darwin": ...
}
// se pueden hacer comparaciones en los casos del switch
number := 42
switch {
case number < 42:
fmt.Println("Smaller")
case number == 42:
fmt.Println("Equal")
case number > 42:
fmt.Println("Greater")
}
// los casos pueden ser listas de valores separados por comas
var char byte = '?'
switch char {
case ' ', '?', '&', '=', '#', '+', '%':
fmt.Println("Should escape")
}
```
### Arrays, Slices, Ranges
#### Arrays
- Tienen longitud fija. Los _arrays_ de longitudes **son tipos diferentes**
- No pueden redimensionarse
- El índice empieza en cero
- Un array **no es un puntero** en Go
```go
var a [10]int // declara un array de enteros (int) con longitud 10. ¡La longitud determina el tipo!
a[3] = 42 // establece el valor de un elemento
i := a[3] // lee el valor de un elemento
// array literals
var a = [2]int{1, 2}
a := [2]int{1, 2} //shorthand
a := [...]int{1, 2} // elipsis -> El compilador infiere la longitud del array
// no hay paso por referencia
arr1 := arr2 // Se hace una copia
arr1 := &arr2 // Se copia la referencia
// multidimensionales
// var variable_name [SIZE1][SIZE2]…[SIZEN] variable_type
a := [3][4]int{
{0, 1, 2, 3} , // initializers for row indexed by 0
{4, 5, 6, 7} , // initializers for row indexed by 1
{8, 9, 10, 11} // initializers for row indexed by 2}
```
#### Slices
- Un _slice_ es una abstracción apuntando a un array.
- Se pueden crear a partir de un array existente, en caso contrario Go creará el array detrás de las bambalinas
- No se especifica la dimensión
- Realmente un _slice_ son tres datos:
- Un puntero a la secuencia de datos en memoria
- Una longitud (_lenght_, `len(a)`) que almacena el número de elementos
- Una capacidad (_capacity_, `cap(a)`) que es el total de posiciones reservadas en memoria
- Cuando se asigna un _slice_ en realidad se copian esos valores, así que **se copia la referencia**
- Si un _slice_ tiene que crecer el compilador normalmente es conservador y **duplica** la capacidad. Eso implicará re-localizaciones del array subyacente en memoria
```go
var a []int // declare a slice - similar to an array, but length is unspecified
var a = []int {1, 2, 3, 4} // declare and initialize a slice (backed by the array given implicitly)
a := []int{1, 2, 3, 4} // shorthand
chars := []string{0:"a", 2:"c", 1: "b"} // ["a", "b", "c"]
var b = a[lo:hi] // creates a slice (view of the array) from index lo to hi-1
var b = a[1:4] // slice from index 1 to 3
var b = a[:3] // missing low index implies 0
var b = a[3:] // missing high index implies len(a)
a = append(a,17,3) // append items to slice a
c := append(a,b...) // concatenate slices a and b
// create a slice with make
a = make([]byte, 5, 5) // first arg length, second capacity
a = make([]byte, 5) // capacity is optional
// create a slice from an array
x := [3]string{"Лайка", "Белка", "Стрелка"}
s := x[:] // a slice referencing the storage of x
```
#### Operaciones sobre _Arrays_ y _Slices_
`len(a)` devuelve la longitud de un _array_ o _slice_ `a`. Es un _built-in_ no un atributo o método del *array*.
```go
// iterar sobre un array o slice
for i, e := range a {
// i es el index, e es el elemento
}
// si no vamos a usar el índice:
for _, e := range a {
// e es el elemento
}
// si solo queremos usar el índice
for i := range a {
}
// Desde Go 1.4 se puede iterar sin variables:
for range time.Tick(time.Second) {
// hacer algo cada segundo
}
```
### Maps
- Los mapas (_maps_) son inmutables
- No se garantiza ningún orden
```go
var m map[string]int
m = make(map[string]int)
m["key"] = 42
fmt.Println(m["key"])
delete(m, "key")
elem, ok := m["key"] // test if key "key" is present and retrieve it, if so
// map literal
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
// iterate over map content
for key, value := range m {
}
```
### Structs
No hay clases en __Go__, solo _structs_. Las _structs_ pueden tener métodos.
```go
// Una struct es un tipo. También es una colección de campos
// Declaración
type Vertex struct {
X, Y int
}
// Creación
var v = Vertex{1, 2}
var v = Vertex{X: 1, Y: 2} // Creación de la struct definiendo sus valores con clave
var v = []Vertex{{1,2},{5,2},{5,5}} // Creación de un slice de structs
// Acceso a miembros de la struct
v.X = 4
// Se pueden declarar métodos sobre structs. La referencia a la struct que recibe el método
// tiene que ir entre la palabra clave 'func' y el nombre del método
// LA ESTRUCTURA SE COPIA EN CADA LLAMADA AL MÉTODO
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// Invocando el método
v.Abs()
// Para los métodos que mutan la estructura usamos punteros a esa struct
// como tipos. De esta forma EVITAMOS LA COPIA de la estructura al invocar el método
func (v *Vertex) add(n float64) {
v.X += n
v.Y += n
}
```
Tenemos un caso especial con los contructores. Si queremos evitar que se construyan objetos sin usar el constructor:
```go
package matrix
type matrix struct { // NO EXPORTADO
....
}
func NewMatrix(rows, cols int) *matrix {
m := new(matrix)
m.rows = rows
m.cols = cols
m.elems = make([]float, rows*cols)
return m
}
```
Como no exportamos la estructura base, solo se pueden construir nuevos objetos a través del constructor.
La función `NewMatrix` puede simplificarse fácilmente:
```go
func NewMatrix(rows, cols, int) *matrix {
return &matrix{rows, cols, make([]float, rows*cols)}
}
```
{{< admonition type=warning title="structs y exportacion" open=true >}}
Si el nombre de la `struct` o de alguno de sus campos empieza por minúscula **no serán visibles** fuera de la propia `struct`
{{< /admonition >}}
**Anonymous structs:**
Más económicas y seguras que usar: `map[string]interface{}`.
```go
point := struct {
X, Y int
}{1, 2}
```
### Punteros
```go
p := Vertex{1, 2} // p es de tipo Vertex
q := &p // q es un puntero a un Vertex
r := &Vertex{1, 2} // r también es un puntero a un Vertex
// El tipo de un puntero a un Vertex es *Vertex
var s *Vertex = new(Vertex) // new crea un puntero a una nueva instancia de Vertex
```
### Interfaces
- Un _interface_ es una especie de "contrato".
- Dentro del _interface_ especificamos las funciones que tiene que soportar un tipo para satisfacer el _interface_
- El _interface_ nos da una capa extra de abstracción, podemos definir una función que procese o devuelva el "tipo" definido por el _interface_
```go
// declaración
type Awesomizer interface {
Awesomize() string
}
// los tipos no declaran en ningún sitio implementar un interface
type Foo struct {}
// los tipos que implementan todos los métodos de un interface, satifacen ese interface implicitamente
func (foo Foo) Awesomize() string {
return "Awesome!"
}
```
### Embedding
There is no subclassing in Go. Instead, there is interface and struct embedding.
```go
// ReadWriter implementations must satisfy both Reader and Writer
type ReadWriter interface {
Reader
Writer
}
// Server exposes all the methods that Logger has
type Server struct {
Host string
Port int
*log.Logger
}
// initialize the embedded type the usual way
server := &Server{"localhost", 80, log.New(...)}
// methods implemented on the embedded struct are passed through
server.Log(...) // calls server.Logger.Log(...)
// the field name of the embedded type is its type name (in this case Logger)
var logger *log.Logger = server.Logger
```
### Errores
No hay gestión de excepciones en __Go__. Las funciones que pueden lanzar un _Error_ simplemente devuelven un valor adicional de tipo `Error`.
El _interface_ `Error` tiene esta pinta:
```go
type error interface {
Error() string
}
```
Una función que puede devolver un error:
```go
func doStuff() (int, error) {
}
func main() {
result, err := doStuff()
if err != nil {
// handle error
} else {
// all is good, use result
}
}
```
### Concurrency
#### Goroutines
Goroutines are lightweight threads (managed by Go, not OS threads). `go f(a, b)` starts a new goroutine which runs `f` (given `f` is a function).
```go
// just a function (which can be later started as a goroutine)
func doStuff(s string) {
}
func main() {
// using a named function in a goroutine
go doStuff("foobar")
// using an anonymous inner function in a goroutine
go func (x int) {
// function body goes here
}(42)
}
```
#### Channels
```go
ch := make(chan int) // create a channel of type int
ch <- 42 // Send a value to the channel ch.
v := <-ch // Receive a value from ch
// Non-buffered channels block. Read blocks when no value is available, write blocks until there is a read.
// Create a buffered channel. Writing to a buffered channels does not block if less than <buffer size> unread values have been written.
ch := make(chan int, 100)
close(ch) // closes the channel (only sender should close)
// read from channel and test if it has been closed
v, ok := <-ch
// if ok is false, channel has been closed
// Read from channel until it is closed
for i := range ch {
fmt.Println(i)
}
// select blocks on multiple channel operations, if one unblocks, the corresponding case is executed
func doStuff(channelOut, channelIn chan int) {
select {
case channelOut <- 42:
fmt.Println("We could write to channelOut!")
case x := <- channelIn:
fmt.Println("We could read from channelIn")
case <-time.After(time.Second * 1):
fmt.Println("timeout")
}
}
```
##### Channel Axioms
- A send to a nil channel blocks forever
```go
var c chan string
c <- "Hello, World!"
// fatal error: all goroutines are asleep - deadlock!
```
- A receive from a nil channel blocks forever
```go
var c chan string
fmt.Println(<-c)
// fatal error: all goroutines are asleep - deadlock!
```
- A send to a closed channel panics
```go
var c = make(chan string, 1)
c <- "Hello, World!"
close(c)
c <- "Hello, Panic!"
// panic: send on closed channel
```
- A receive from a closed channel returns the zero value immediately
```go
var c = make(chan int, 2)
c <- 1
c <- 2
close(c)
for i := 0; i < 3; i++ {
fmt.Printf("%d ", <-c)
}
// 1 2 0
```
### Printing
```go
fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // basic print, plus newline
p := struct { X, Y int }{ 17, 2 }
fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc
s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable
fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // c-ish format
s2 := fmt.Sprintf( "%d %f", 17, 17.0 ) // formatted print to string variable
hellomsg := `
"Hello" in Chinese is 你好 ('Ni Hao')
"Hello" in Hindi is नमस्ते ('Namaste')
` // multi-line string literal, using back-tick at beginning and end
```
### Reflection
#### Type Switch
A type switch is like a regular switch statement, but the cases in a type switch specify types (not values), and those values are compared against the type of the value held by the given interface value.
```go
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
```
### Snippets
#### HTTP Server
```go
package main
import (
"fmt"
"net/http"
)
// define a type for the response
type Hello struct{}
// let that type implement the ServeHTTP method (defined in interface http.Handler)
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello!")
}
func main() {
var h Hello
http.ListenAndServe("localhost:4000", h)
}
// Here's the method signature of http.ServeHTTP:
// type Handler interface {
// ServeHTTP(w http.ResponseWriter, r *http.Request)
// }
```
## Misc
* Para ver el entorno usamos `go env`
## TDD con Go
* [TDD with Go](https://leanpub.com/golang-tdd/read)
Para que funcionen correctamente los comandos `go build`, `go clean` y `go test` parece que es necesario haber definido correctamente `go.mod` (con `go mod init`)
`go test` buscará ficheros nombrados como `*_test.go` para ejecutarlos.
Dentro de esos de test habrá bloques funcionales con la firma: `func TestXxxx(t *testing.T)`
Los test fallidos se informan con `t.Errorf`
### Ciclo de test
El ciclo de trabajo se resume en _**Red, Green, Refactor**_.
* Para implementar una nueva funcionalidad el primer paso debe ser escribir los test que capturan los requisitos de esa nueva funcionalidad. Estos test obviamente fallarán (_Red_ o rojo de fallo).
* La segunda fase consiste en implementar el código necesario para cumplir con los test y queden todos en verde (_Green_). El código debe minimizarse para cumplir los test sin "sobreimplementar" nada.
* El último paso es el _refactoring_. **No** implementamos nueva funcionalidad en este paso, se trata simplemente de hacer un código de calidad re-escribiendo el código de la segunda fase. **No** es un paso opcional, es imprescindible revisar el código y garantizar la coherencia y calidad del mismo.
### Vicios en TDD
1. Escribir demasiados test a la vez
2. Concentrarse exclusivamente en los _happy path_ y la cobertura del código
3. No considerar los diferentes escenarios
4. Demasiadas comprobaciones (_asserts_) en un sólo test
5. Probar cosas diferentes en el mismo caso de prueba
6. Escribir test triviales para mantener el código cubierto
7. No ejecutar los test con frecuencia
8. No seguir siempre las tres fases. Especialmente implementando código sin escribir antes el test
9. Hacer _asserts_ que no prueban nada
10. Crear test difíciles de mantener
### Dobles de prueba (_test doubles_)
Una implementación simplificada de algún tipo para facilitar las pruebas
Casos posibles:
_**Dummies**_
: Tipos sin ningún comportamiento, se implementan únicamente para cumplir con la firma de alguna función que queremos probar
_**Stubs**_ ("breves")
: Tipos que implementan el comportamiento mínimo para pasar un test
***Mocks*** (parodias)
: Implementaciones parciales que permiten definir como suponemos que serán los métodos sobre el tipo
***Spies*** (espías)
: Implementaciones parciales que nos permiten comprobar que métodos han sido invocados
***Fakes*** (falsificaciones)
: Implementaciones ligeras pero completas, por ejemplo implementar una base de datos en memoria a efectos de pruebas
## Paquetes útiles
### `flag`
Este paquete nos permite leer parámetros pasados en la llamada al programa por linea de comandos.
### `encoding/json`
Este paquete nos permite salvar o recuperar estructuras de datos desde ficheros _json_
{{< admonition type=warning title="Referencias" open=false >}}
* [json con golang](https://golangdocs.com/json-with-golang)
* [read a json file](https://golangdocs.com/golang-read-json-file)
* [Parse json to a nested struct](https://penthaa.medium.com/golang-how-to-parse-json-data-into-a-nested-struct-29be89ce2ae8)
{{< /admonition >}}
**_marshalling_**
: El proceso de codificar nuestra estructura de datos en _json_.
**_unmarshalling_**
: El proceso inverso al anterior para pasar de json a una estructura de datos
#### Arrays y Slices
Podemos codificar en _json_ arrays o slices de _Go_
```go
package main
import (
"encoding/json"
"fmt"
)
type Book struct {
Name string
Author string
}
func main() {
//--------------------------------------------------
// Marshalling
book := Book{"C++ programming language", "Bjarne Stroutsrup"}
my_json, err := json.Marshal(book)
if err != nil {
fmt.Println(err)
}
fmt.Printf("Json for book is: %s\n", string(my_json)) // {"Name":"C++ programming language","Author":"Bjarne Stroutsrup"}
//--------------------------------------------------
// Unmarshalling
codString := `{"Name":"The Name of the Wind","Author":"Patrick Rothfuss"}`
var cod Book
err = json.Unmarshal([]byte(codString), &cod)
if err != nil {
fmt.Println(err)
}
fmt.Printf("Book for json is: %v\n", cod)
//--------------------------------------------------
// Arrays and Slices
// You can marshall/unmarshall to array or slice
var books []Book
booksJson := `[{"Name": "Hacking for Dummies", "Author": "Kevin Beaver"},
{"Name": "Kerberos", "Author": "Jason Garman"}]`
err = json.Unmarshal([]byte(booksJson), &books)
if err != nil {
fmt.Println(err)
}
fmt.Printf("Slice for json is: %v\n", books)
some_books := []Book{
{Name: "Libro Uno", Author: "Author Uno"},
{Name: "Libro Dos", Author: "Author Dos"},
}
some_books_json, err := json.Marshal(some_books)
fmt.Printf("json for slice is: %v\n", string(some_books_json))
}
```
#### Atributos "a medida" para _json_
Podemos añadir atributos _json_ a una `struct` que vamos a leer o a salvar con _json_.
```go
package main
import (
"encoding/json"
"fmt"
)
type Book struct {
Name string `json:"title"` // IMPORTANTE: Nada de espacios al definir los atributos
Author string `json:"artist"`
}
func main() {
//--------------------------------------------------
// Marshalling
book := Book{"C++ programming language", "Bjarne Stroutsrup"}
my_json, err := json.Marshal(book)
if err != nil {
fmt.Println(err)
}
fmt.Printf("Json for book is: %s\n", string(my_json)) // {"title":"C++ programming language","artist":"Bjarne Stroutsrup"}
}
```
Además de cambiar los nombres de los campos podemos especificar un par de atributos `json` más:
- `.omitempty`: nos permite saltarnos los datos si el campo está vacio
- `"-"` : nos permite saltarnos este campo por completo
```go
type userdata struct {
login string `json:"username"`
real_name string `json:"name, .omitempty"`
password string `json:"-"`
}
```
#### _json_ y maps
Siempre podemos cargar el _json_ en un `map`, especialmente si no están muy bien estructurados:
```go
package main
import (
"fmt"
"encoding/json"
)
func main() {
unstructuredJson := `{"os": {"Windows": "Windows OS","Mac": "OSX","Linux": "Ubuntu"},"compilers": "gcc"}`
var result map[string]interface{}
json.Unmarshal([]byte(unstructuredJson), &result)
fmt.Println(result["os"]) // map[Linux:Ubuntu Mac:OSX Windows:Windows OS]
}
```
Por supuesto podemos también salvarlos en _json_:
```go
package main
import (
"fmt"
"encoding/json"
)
type Address struct {
Street string
City string
}
type Person struct {
Name string
Address Address
}
func main() {
p := Person{
Name: "Sherlock Holmes",
Address: Address{
"22/b Baker street",
"London",
},
}
str, err := json.Marshal(p)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(str)) // {"Name":"Sherlock Holmes","Address":{"Street":"22/b Baker street","City":"London"}}
}
```
### `os/exec`
<https://www.darrencoxall.com/golang/executing-commands-in-go/>
Este paquete es parte de la biblioteca estándar de _Go_. Nos permite ejecutar comandos de sistema.
Tres casos típicos:
- Lanzar un comando y capturar su salida
- Lanzar un comando y comprobar el _exit code_
- Lanzar comandos de larga duración
```go
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"syscall"
)
// printCommand prints command
func printCommand(cmd *exec.Cmd) {
fmt.Printf("==> Executing: %s\n", strings.Join(cmd.Args, " "))
}
// printError print human friendly error objects
func printError(err error) {
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("==> Error: %s\n", err.Error()))
}
}
// printOutput prints command output
func printOutput(outs []byte) {
if len(outs) > 0 {
fmt.Printf("==> Output: %s\n", string(outs))
}
}
// main el cuerpo ppal del programa
func main() {
// Create an *exec.Cmd
// Capture Stdout and Stderr together
cmd := exec.Command("echo", "Called from Go!")
printCommand(cmd)
output, err := cmd.CombinedOutput() // runs cmd and return combined output
printError(err)
printOutput(output) // => go version
// Create an *exec.Cmd
cmd = exec.Command("go", "version")
// Stdout buffer
cmdOutput := &bytes.Buffer{}
// Attach buffer to command
cmd.Stdout = cmdOutput
// Execute command
printCommand(cmd)
err = cmd.Run() // will wait for command to return
printError(err)
// Only output the commands stdout
printOutput(cmdOutput.Bytes())
// create a command that will fail
cmd = exec.Command("ls", "/imaginary/dir")
var waitStatus syscall.WaitStatus
if err := cmd.Run(); err != nil {
printError(err)
// Did the command fail because of an unsuccessful exit code
if exitError, ok := err.(*exec.ExitError); ok {
waitStatus = exitError.Sys().(syscall.WaitStatus)
printOutput([]byte(fmt.Sprintf("%d", waitStatus.ExitStatus())))
}
} else {
// Command was successful
waitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus)
printOutput([]byte(fmt.Sprintf("%d", waitStatus.ExitStatus())))
printOutput(cmdOutput.Bytes())
}
}
```
Para el último caso _comandos de larga duración_, no queremos que nuestro programa espere al resultado del comando. Es mejor hacerlo de forma asíncrona.
```go
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"time"
)
// printError print human friendly error objects
func printError(err error) {
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("==> Error: %s\n", err.Error()))
}
}
// printOutput prints command output
func printOutput(outs []byte) {
if len(outs) > 0 {
fmt.Printf("==> Output: %s\n", string(outs))
}
}
// main() programa ppal
func main() {
cmd := exec.Command("cat", "/dev/random") // creamos un comando
randomBytes := &bytes.Buffer{} // creamos un buffer
cmd.Stdout = randomBytes // asociamos la salida estándar al buffer
err := cmd.Start() // Lanzamos el comando de forma asíncrona
printError(err)
// Create a ticker that outputs elapsed time
ticker := time.NewTicker(time.Second)
go func(ticker *time.Ticker) {
now := time.Now()
for _ = range ticker.C {
printOutput(
[]byte(fmt.Sprintf("%s", time.Since(now))),
)
}
}(ticker)
// Create a timer that will kill the process
timer := time.NewTimer(time.Second * 4)
go func(timer *time.Timer, ticker *time.Ticker, cmd *exec.Cmd) {
for _ = range timer.C {
err := cmd.Process.Signal(os.Kill)
printError(err)
ticker.Stop()
}
}(timer, ticker, cmd)
// Only proceed once the process has finished
cmd.Wait()
printOutput(
[]byte(fmt.Sprintf("%d bytes generated!", len(randomBytes.Bytes()))),
)
}
```
### `tcell`
Este paquete nos permite implementar interfaces de usuario basados en texto.
Instalamos:
```bash
go get -u github.com/gdamore/tcell
```
Recomiendan mirar primero el ejemplo incluido `mouse`
## Casos prácticos
### Leer y escribir json
Básico:
```go
package main
import (
"encoding/json"
"fmt"
)
// main ...
func main() {
x := map[string]string{
"foo": "bar",
}
data, _ := json.Marshal(x)
fmt.Printf("Data contains:\n%s\n", (data))
}
```
Result:
```bash
Data contains:
{"foo":"bar"}
```
Un poco más complicado:
```go
package main
import (
"encoding/json"
"fmt"
)
// main ...
func main() {
type person struct {
Name string `json:"name"`
Age int `json:"age"`
Description string `json:"descr,omitempty"` // DON'T use spaces in tags
secret string // Unexported fields are Unmarshaled
}
x := person{
Name: "Bob",
Age: 32,
secret: "Shhh!",
}
data, _ := json.Marshal(x)
fmt.Printf("Data contains:\n%s\n", (data))
}
```
Un ejemplo de *unmarshalling*, aunque siempre es preferible usar una `struc` si conocemos de antemano la estructura de los datos:
```go
package main
import (
"encoding/json"
"fmt"
)
// main test json
func main() {
data := []byte(`{"foo":"bar"}`)
var x interface{}
_ = json.Unmarshal(data, &x)
fmt.Printf("x contains:\n%s\n", x)
}
```
Un ejemplo de :
```go
```
## Best Practices
## Para estudiar
- <https://stackoverflow.com/questions/27178635/cast-a-struct-pointer-to-interface-pointer-in-golang>
- [Advanced Command Execution in Go](https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html)
- [Executing shell commands in Go](https://medium.com/rungo/executing-shell-commands-script-files-and-executables-in-go-894814f1c0f7)