Mostrando las entradas con la etiqueta Go. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Go. Mostrar todas las entradas

16/07/2022

Guorkshop Go 1. Organización del código. Estructura de proyectos, módulos y paquetes

Si va a escribir un programa muy simple, en un único archivo, y no espera reusar el código, entonces no se preocupe de la organización de su proyecto y escriba todo su código en un único archivo fuente. Pero si esta trabajando en resolver un problema del mundo real, entonces hay una gran posibilidad de que necesite diseñar su proyecto de forma de poder reusar la mayor cantidad de código posible. Si este último es su caso, entonces preocupese.

Workshop. Fotografía por Daniel Mee a través de Flickr. Licencia CreativeCommons

Este es el primero de los prometidos guorkshops. Puede encontrar el guorkshop 0 aquí. Puede encontrar los otros guorkshops aca.

Si bien nadie le obliga a seguir o usar una forma de estructurar su proyecto, y ciertamente, si su proyecto es simple, o una prueba de conceptos, la estructura que presentaremos en este workshop puede ser innecesaria y un único archivo main.go donde escribir su código basta y sobra. Solo tenga presente que a medida que su código crece es importante que esté bien estructurado, de lo contrario corre el riesgo de terminar con un desorden de proporciones, con dependencias ocultas, estado global y otros malos olores.

Go nos provee con dos formas principales de organizar el código, módulos y paquetes.

Módulos

Los módulos de Go contienen uno o mas paquetes almacenados en una estructura de directorio y nos permiten entre otras cosas controlar las versiones de los paquetes que contiene, y con esto compartir o usar las funcionalidades de sus componentes.

Los módulos de Go tienen en su directorio raíz dos archivos especiales; go.mod y go.sum, donde el archivo go.mod define la ruta del modulo, indica la versión de Go con la que el módulo trabaja, y declara los paquetes de los que depende.

El archivo go.sum es generado automáticamente por Go cuando empezamos a agregar dependencias Contiene el checksum de las dependencias del módulo para determinar que estas no hayan sido modificadas y nosotros no deberiamos modificarlo manualmente.

Con ciertos reparos, podríamos decir que cada vez que vaya a empezar un nuevo proyecto de Go debería crear un módulo.

Para crear un módulo de Go solo tenemos que crear un directorio que será la raíz del proyecto, y usar go mod init nombre_del_modulo.

$ mkdir mi_modulo
$ cd mi_modulo
$ go mod init mi_modulo

Se considera buena practica que el nombre del módulo contenga su ruta de importación para que pueda compartirse usando las herramientas de Go. Esta ruta de importación es la ruta que Go usa para importar desde otros los paquetes contenidos en el módulo.

La especificación de Go indica sobre la ruta de importación:

La ruta de importación es el nombre canónico de un módulo, declarado con la directiva de módulo en el archivo go.mod. La ruta de un módulo es el prefijo para importar los paquetes que contiene.

Se ha vuelto práctica de la comunidad de desarrolladores el usar la url del repositorio que contiene el módulo, incluyendo el username del autor si se tratara de un repositorio como los de github, con la forma host/usuario/repositorio. Por ejemplo github.com/profe-ajedrez/my-awesome-module corresponde al módulo my-awesome-module del usuario profe-ajedrez, alojado en github.com. Otra convención muy usada es la forma dominio organizacion/nombre del modulo, por ejemplo calcutarasterware.com/a-pandaric-module.

Ejemplo:

Crearemos un módulo llamado mi_modulo y lo esperamos alojar en github en el repositorio del mismo nombre. Usaré mi usuario de github para este ejercicio.

$ mkdir mi_modulo
$ cd mi_modulo
$ go mod init github.com/profe-ajedrez/mi_modulo

El comando go mod init github.com/profe-ajedrez/mi_modulo crea al módulo en nuestro directorio local con el nombre github.com/profe-ajedrez/mi_modulo, note que aún no hemos creado ningún repositorio en github, generando al archivo go.mod, el cual contiene la ruta de importación del módulo y la versión de Go que usa nuestro proyecto.

# cat go.mod
module github.com/profe-ajedrez/mi_modulo

go 1.18

Dandole estructura a nuestro módulo

La comunidad de desarrolladores de Go ha probado con distintas aproximaciones a la hora de organizar el código de los proyectos, y si bien existen varias formas de lograr esta organización, creemos que la mas extendida es la siguiente, que si bien no es un estándar oficial definido por el equipo de desarrolladores de Go, es un conjunto de patrones históricos en el ecosistema del lenguaje.

directorio raíz del proyecto o módulo/
  cmd/
  internal/
  pkg/

  go.mod
  go.sum

  • El subdirectorio cmd/ contiene los paquetes main que son los puntos de entrada de la aplicación, pudiendo haber varios de ellos, cada uno en su propio subdirectorio cuyo nombre es el mismo que el del binario que generaran.
  • El subdirectorio internal/ contiene el código privado del módulo, es decir, los paquetes que se encuentren en este directorio no se podrán importar desde otros módulos, pero si pueden ser usados en el módulo actual.
  • El subdirectorio pkg/ contiene los paquetes que si serán importables desde otros módulos.

Paquetes

Los paquetes contienen archivos fuente y otros paquetes, y se usan para organizar código Go mejorando su legibilidad y reusabilidad, encapsulando el código de los archivos fuente que se encuentran en un mismo directorio.

Los paquetes corresponen a directorios que contienen archivos fuente y otros paquetes, y se usan para organizar código Go logrando, si se usan bien, mejorar su legibilidad y reusabilidad. Esto lo hacen encapsulando el código de los archivos fuente que se encuentran en un mismo directorio. Por lo tanto, todos loas archivos fuentes que se encuentren ene l mismo directorio deben pertenecer al miosmo paquete, en caso contrario ni siquiera podremos compilar nuestro código.

Remarcamos encapsulando debido a que en Go el paquete es la unidad de encapsulación en oposición a la clase que ofrecen otros lenguajes.

En Go, encontramos dos tipos de paquetes:

1 El paquete main que se usa para generar el ejecutable. Es responsable de contener a la función main que indica el punto de entrada a la aplicación.

// main.go
package main

import (
	"fmt"
)


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

2 El paquete orientado a reusar código, o paquete de librería que contiene código que será reusado.

// recepcion/recepcion.go
package recepcion

import (
	"fmt"
)


func Recibir() {
	fmt.Println("Recibiendo")
}

Podemos ver que los archivos fuente de Go empiezan con una línea indicando el paquete al que pertenece. Todo código Go debe pertenecer a uno, siendo nombrado generalmente con el mismo nombre que el del directorio que lo aloja.

El código dentro de un paquete puede referir a cualquier elemento (variables, constantes, funciones y otros tipos de dato) definido dentro de el, mientras que código de otros paquetes solo puede referir a los elementos que hayan sido exportados. Esta referencia siempre incluye el nombre el paquete como un prefijo; puertas.Abrir() refiere a función exportada Abrir del paquete puertas.

Effective Go recomienda que los nombres de paquete sean cortos, concisos y evocativos, y presenta la convención de que los nombres de paquetes sean palabras únicas en minúscula, sin subrayados o mixedCap.

Otra convención es que el paquete tenga el mismo nombre que su directorio fuente.


Gracias por aguantar hasta este punto. ¡Ahora ensuciemonos las manos!

Workshop Hora 1

Para realizar este workshop debe tener Go instalado, usar la terminal de su sistema y estar ubicado en algún directorio de trabajo.

1 En su terminal cree una nuevo directorio para el módulo con el nombre workshop_1
2 Acceda a la nueva carpeta
3 Cree un módulo de nombre workshop_1
4 Dentro del módulo cree la estructura de directorios presentada arriba
5 Abra el directorio con una instancia de su editor de texto favorito
6 Dentro del directorio internal cree el directorio xmath
7 En internal/xmath/ cree el archivo fuente xmathsourcefile.go
8 En el archivo internal/xmath/xmathsourcefile.go agregue el siguiente fragmento de código
package xmath

func SubstractInt(a int, b int) int {
  return a - b
}

9 En el directorio pkg cree el directorio xmathclient
10 En el directorio pkg/xmathclient/ cree el archivo xmathclientsourcefile.go
11 En el archivo pkg/xmathclient/xmathclientsourcefile.go agregue el siguiente fragmento de código
package xmathclient

import (
	"fmt"
	"workshop_1/internal/xmath"   // ¡Esto es importante! Note como se importa el paquete xmath
)

func Client() {
	fmt.Println(xmath.SubstractInt(10, 5))
}

Observe como se importa al paquete xmath. ¿Recuerda cuando comentamos sobre la ruta de importación del módulo? Pues se trataba de esto, la ruta de importación del módulo es la raíz de donde parte la ruta para encontrar los paquetes que importamos. Note que la ruta de importación del paquete xmath, incluye al directorio internal

12 En el directorio raíz del módulo, cree el archivo main.go y agreguele el siguiente código
package main

import "workshop_1/pkg/xmathclient" // Note como desde main importamos al paquete xmathclient

func main() {
	xmathclient.Client()
}

¿Vio como desde main.go importamos al paquete xmathclient? ¡Exacto! a través de la ruta de importación del módulo pasando por el directorio pkg que lo aloja.

13 En el directorio raíz del módulo, en la terminal ejecute el programa con go run main.go. Debería obtener la salida 5
  • ¿Que hemos hecho?

Hemos creado un módulo Go llamado mi_modulo, en el hemos creado la estructura de directorios propuesta para proyectos Go. En el directorio internal, creamos el directorio/paquete xmath, y en el directorio pkg creamos el directorio/paquete xmathclient. Finalmente creamos el archivo main.go en la raíz del directorio y lo ejecutamos para ver la salida del programa.

Muy bien, ahora rompamos algunos huevos y agreguemos un pequeño error que resolveremos posteriormente.

14 Modifique el archivo internal/xmath/xmathsourcefile.go para que se vea como el siguiente código
package xmath

func SubstractInt(a int, b int) int {
  return a - b
}

func addInt(a int, b int) int {
  return a + b
}

15 Modifique el archivo pkg/xmathclient/xmathclientsourcefile.go de la siguiente manera
package xmathclient

import (
	"fmt"
	"workshop_1/internal/xmath"
)

func Client() {
	fmt.Println(xmath.SubstractInt(10, 5))
	fmt.Println(xmath.addInt(10, 5))
}


16 Ejecute al programa en la terminal, en el directorio raíz del módulo con go run main.go. Debería ver el mensaje undefined: xmath.addInt

Esto ocurre debido a que el nombre de la función addInt del paquete xmath empieza con una letra minúscula, lo que le indica al compilador que es una función privada del paquete, esto es, solo puede accederse a ella desde dentro del paquete donde fue declarada. Código alojado en otros paquetes no podrá acceder a ella.

15 Repare el error para obtener en consola la salida Cambiando la inicial de la función addInt por una letra mayúscula, en su declaración y en su llamada.

Una vez corregido el problema, al ejecutar el programa debería obtener la siguiente salida:

5
15

En este punto ya debería tener un módulo de Go funcionando correctamente y haber entendido la forma de importar y usar paquetes.

Debe recordar
  • Los módulos almacenan paquetes y nos permiten versionar nuestro código
  • Paquetes encapsulan nuestro código Go y definen ámbito
  • El paquete main indica cual archivo será convertido en ejecutable
  • import permite usar funcionalidad de un paquete en otros paquetes
  • Para exportar un identificador debemos iniciar su nombre con una letra mayúscula

Hello Go

Hello Go

Un pequeño recordatorio. Si de verdad completó el tour de Go puede pasar de inmediato al siguiente artículo.

Tomemos el siguiente programa (puede ejecutarlo en el Go playground en https://go.dev/play/p/lBlmIOs1svX)

package main



import "fmt"

var myGlobal = 12

func main() {
	fmt.Println(myGlobal)
}

Y analisemoslo línea a línea. Primero nos encontramos con:

package main

Lo cual es la declaración de nuestro paquete. En terminos planos, un paquete es un directorio que contiene archivos fuente de go u otros paquetes. Toda función, variable o tipos que definamos, debe pertenecer a un paquete. Podemos verlos como contenedores que guardan piezas reusables de código.

El package main es un paquete especial que le indica al compilador Go que el archivo será convertido en un ejecutable. Si ud. quiere ejecutar el código de su paquete directamente, necesita llamarlo main.

import "fmt"

import permite incorporar código de otros paquetes. La línea anterior dice Trae el código encapsulado en el paquete fmt y dejame usarlo a través del nombre del paquete. Los imports aplican solo en el archivo en el cual se declaran, si necesita importar el mismo paquete a través de distintos archivos fuente, debe declarar el import en cada uno de ellos.

var myGlobal = 12

En esta línea, declaramos una variable de paquete privada. Es una variable de paquete porqué fue definida en el ámbito de este sin estar dentro de alguna función. Por lo tanto todo el código que pertenezca al mismo paquete podrá acceder a ella aunque sea desde un archivo fuente distinto al que fue declarada

Note que la variable myGlobal comienza con una letra minúscula. En Go, si queremos que nuestras definiciones (variables, funciones, otros) no puedan ser accedidos desde fuera del paquete, sean privadas de este, simplemente debemos comenzarlas con una letra minúscula.

  func main() {

En esta línea, definimos una función, pero no cualquier función. main es una función especial para Go, y le indica al compilador el punto de partida de nuestra aplicación. Es decir, nuestra aplicación comenzará en la función main del paqeute main.

fmt.Println(myGlobal)

Esta línea imprime el contenido de la variable myGlobal usando la función Print del paquete fmt. Note como se accede a la funcionalidad ofrecida por un paquete usando la notación nombrePaquete.nombreFuncion

Los Guorkshops de Go del Programador Pobre

Pues la cosa detrás de estos guorkshops es que sean un grupo de artículos donde podamos ensuciarnos las manos trabajando en algunos conceptos e ideas que tengo dando vueltas hace rato en la cabeza sobre el lenguaje Go. Si tuviera que poner un objetivo concreto, sería el repasar las caracteristicas principales de Go aprendiendo buenas prácticas, haciendo cosas y usando un leguaje coloquial que permita presentar contenidos sin tanto término rimbombante. En pocas pálabras, Go al hueso (aunque suene gracioso!).

Workshop. Fotografía por Daniel Mee a través de Flickr. Licencia CreativeCommons

Los llamo guorkshops, con la palabra en Inglés mal escrita intentando darle un toque artesanal y DIY, y también para no tomarme demasiado en serio y permanecer siempre en el lado más humilde, pues estos guorkshop per se tienen que ser opinionados, en el sentido de que a través de la experiencia personal y de la comunidad presenta prácticas que se consideran correctas las cuales pueden ir cambiando con el correr del tiempo.

La metodología que uso es incremental y repetitiva a propósito, pues habiendo sido varios años profesor de ajedréz, es la que me ha dado mejores resultados, entregando ídeas y habilidades de forma acumulativa. Cosas que podrían parecer errores en un workshop, son usadas para ejemplificar una ídea específica de este y son corregidas en los siguientes junto con un comentario explicativo del error o mala práctica que ejemplifica.

Sin perjuicio de lo anterior, aunque a veces no lo parezca, soy un ser humano. Me reservo el derecho a equivocarme.

Cabe señalar que cada vez me vuelvo un mas firme creyente del Manifiesto, y creo que lo notará en los guorkshops.

Requisitos previos

Go instalado

Dirijase a https://go.dev/doc/install e instale la última versión estable del lenguaje correspondiente a su sistema operativo siguiendo las instrucciones que el sitio le ofrece.
Si usa Linux, recomiendo instalar Go manualmente y no usar al manejador de paquetes de su distribución o utilidades como Snap, para garantizar el uso de la última versión estable del lenguaje.

Tour de Go

Go es un lenguaje muy sencillo de aprender, pero como todo lo que vale la pena, es díficil lograr maestría con el. El primer paso para su dominio debería siempre ser el Tour de Go que enseña de manera interactiva los aspectos básicos del lenguaje.

En estos workshops, asumimos que ya ha completado el tour de Go. Si aún no lo ha hecho completelo y luego regrese.

Uso de terminal

En estos workshops asumimos que tiene un manejo básico de la terminal y los comando de su sistema operativo. Basta con que sepa abrír una línea de comandos, navegar a través de directorios, listar archivos y crear subdirectorios.

Herramientas

IDES

La wiki oficial de Go tiene una pagina dedicada a presentar y configurar ides para hacer más eficiente el desarrollo con este lenguaje. Ud puede usar el de su preferencia, pero los ejemplos de este workshop serán escritos en Visual Studio Code usando el plugin oficial

Toolset

Go viene con una serie de herramientas para ayudar a los desarrolladores a ser productivos. Estas pueden llamarse desde la línea de comandos o integrarse con su ide favorito (Esto lo hace el plugin oficial de Visual Studio Code. por ejemplo) Aún así, no está de más conocerlas y saberlas usar desde la terminal.

Puede obtener en su terminal esta lista de herramientas con:

$ go tool

El toolset de Go es muy completo y tiene muchas otras herramientas. Puede verlas en su pagina de documentación oficial


Enlaces directos a los guorkshops

08/04/2022

Structs y algunas notas

Los tipos de datos de toda la vida; enteros, cadenas de texto, booleanos, etc. funcionan muy bien para realizar tareas triviales, pero el desarrollo de software no siempre trata acerca de encontrar el mayor de dos números. Hay muchos problemas que resolver y muchas entidades que modelar.

Una struct es un tipo de datos definido por el usuario que agrupa un conjunto de campos que también tienen tipos definidos.

En efecto, gran parte de la solución de un problema, cuando programamos, pasa por representar de manera adecuada a una entidad del mundo real y a su flujo de vida. Pues bien, ¿De que forma representamos un automóvil y su flujo de fabricación? ¿Como modelamos una factura o un chocolate? y más allá, ¿Como modelamos dos chocolates de distintas marcas?

Cada lenguajes de programación provee su propia forma de lidiar con este problema, la cual se puede resumir en algún tipo de datos personalizado. Algunos ponen a nuestra disposición arreglos asociativos, otros clases, etc. Go por su parte nos deja utilizar structs, los cuales ya mencionabamos en un artículo anterior.

Una struct es un tipo de datos definido por el usuario (en el sentido de que quien programa es el usuario) que agrupa un conjunto de campos que también tienen tipos definidos.

Retomando el ejemplo del automóvil, si lo abstraemos, vemos que posee atributos cómo marca, modelo, año de fabricación, color, etc. Usemos este caso para ejemplificar la declaración de structs

Declaración de structs
  

type Automovil struct {
    Marca string
    Color string
    Ano int
}
  

playground

De esta forma declaramos una struct type conteniendo los campos Marca, Color y Ano cómo una estructura de datos personalizada llamada Automovil.


  
  
package main

import "fmt"

type Automovil struct {
	Marca string
	Color string
        Ano int
}

func(a Automovil) Acelerar() {
    fmt.Println("Este automóvil " + a.Marca + " de color " + a.Color + " esta acelerado")
}

func main() {
	myCar := Automovil{
		Marca: "Suzuki",
		Color: "Rojo",
                Ano: 1999,      // Notese la coma al final de la última línea
	}
          
	fmt.Println("Mi Auto: ", myCar)
 
} 
  

playground

¿Se dio cuenta de cómo definimos una variable de tipo struct Automovil justo al principio de la función Main? Esta forma de inicialización es conocida con el nombre de inicialización por struct literal. También podemos inicializar variables con nuestro struct con el estamento new


  
	var myCar *Automovil
	myCar = new(Automovil)
	myCar.Marca = "Suzuki"
	myCar.Color = "Rojo"
	myCar.Ano = 1999

	fmt.Println("Mi Auto: ", myCar)

  

playground

Asociando métodos a nuestro struct

En Go, las structs puede tener métodos asociados.


  
package main

import "fmt"

type Automovil struct {
	Marca string
	Color string
	Ano   int
}

func (a Automovil) Acelerar() {
	fmt.Println("Este automóvil " + a.Marca + " de color " + a.Color + " esta acelerado")
}

func main() {
	myCar := Automovil{
		Marca: "Suzuki",
		Color: "Rojo",
		Ano:   1999,
	}

	myCar.Acelerar()
}
  


playground

 

Structs, clases y herencia
Go usa la composición para no repetir la implementación de comportamientos, que muy resumidamente, es armar nuestros objetos a partir de otros

Ud podrá pensar; Si un struct tiene campos y también métodos ¿En que se diferencia de una clase? No se equivoque; son parecidos pero no iguales, ambos esconden ideas diferentes en su concepción aunque a nosotros cómo usuarios nos permiten hacer cosas parecidas.

La primera diferencia es que Go no implementa mecanismos de herencia, con esto, surge la segunda diferencia, el encapsulamiento en Go se gestiona a nivel de paquetes, donde un elemento es exportable o no según si su letra inicial es mayúscula o minúscula; con la consecuencia de la no existencia del ámbito protected ¡Porque no existe herencia!

Como no hay herencia, necesitamos una forma para no repetir la implementación de comportamiento. En Go se tomó la decisión de diseño de hacerlo por la vía de la composición, que muy resumidamente es armar nuestros objetos a partir de otros. Si Ud ha trabajado con React o Flutter, entonces este concepto no le será nuevo, pues este patrón forma parte intrínseca de ellos para la creación de componentes.

Un pequeña nota sobre composición sobre herencia

Hay una rica y variada discusión acerca de composición sobre herencia, la cual es muy grande y escapa de las pretenciones de este artículo. Si no ha escuchado sobre el asunto, baste decir que podemos pensar cuando hablamos de composición en una relación del tipo tiene un, esta formado por, mientras que cuando hablamos de herencia, nos referimos a una relación del tipo es un. Fuente: McConnell, Steve (2004). Code Complete: A Practical Handbook of Software Construction. Microsoft Press; 2nd edición.

También puede encontrar una jugosa discusión sobre el tema en este hilo de Stackoverflow.



Composición

Para los diseñadores de Go la decisión de optar por la composición como la forma idiomática de compartir comportamiento y estado es tan importante, que el lenguaje nos permite incrustar en un struct en otro. Ej.


  
package main

import "fmt"

// Automovil el struct base que incrustaremos
type Automovil struct {
	Marca string
	Color string
	Ano   int
}

func (a Automovil) Acelerar() {
	fmt.Println("Este automóvil " + a.Marca + " de color " + a.Color + " esta acelerado")
}

// Delorean Porqué si vas a construir una máquina del tiempo ¿por qué no hacerlo con estilo? Dr. Emmet Lathrop Brawn
type Delorean struct {
	Automovil
}

func (d Delorean) TimeSkip() {
	fmt.Println("Viajando al 5 de noviembre de 1955 ¿Esa es la granja de Peabody?")
}

func NewDelorean() Delorean {
	d := Delorean{}
	d.Marca = "Delorean"
	d.Color = "Plata"
	d.Ano = 1988
	return d
}

func main() {
    myCar := Automovil{
        Marca: "Suzuki",
        Color: "Rojo",
        Ano:   1999,
    }

    myCar.Acelerar()
    
    autoGenial := NewDelorean()
    autoGenial.Acelerar()
    autoGenial.TimeSkip()
}

  


playground

 

Esto nos abre la puerta a sofisticadas formas de modelar las entidades que son parte de nuestro modelo de negocio. Pero eso es conversaciónm para otro artículo.

16/03/2022

Punteros en Go


Si completó el Tour de Go, recordará que Go posee punteros, y que un puntero contiene la dirección de memoria en la que está contenido un valor.

Un puntero es simplemente una variable que contiene la dirección de memoria de otra variable

Si declaramos en un programa Go una variable de tipo int y otra variable como un puntero a int se vería así:


  
    package main
    
    func main() {
        var i int  = 42
        var p *int = &i
    }
  

Donde en la variable i almacenamos el número 42, mientras que en la variable p almacenamos la dirección de memoría donde está alojada la variable i


diagrama 1

¿Se fijó en el ampersand & antes de la i en la definición de la variable puntero? Su nombre es operador de referencia y es la forma de obtener el puntero a un elemento en Go.

Seguramente también observó el asterisco * en la declaración del tipo de la variable puntero p. Es el operador de dereferencia o puntero, indica que la variable contiene un puntero, y también sirve para obtener el dato almacenado en la dirección de memoria a la que el puntero apunta.


  
    package main
    
    import "fmt"
    
    func main() {
        var i int  = 42
        var p *int = &i
        
        fmt.Println(*p) // prints 42
        
    }
    
  
playground

new

Para nuestra conveniencia, existe otra forma de obtener un puntero. la función new, la cual toma un tipo como argumento y devuelve un puntero a él. La expresión new(T) crea una variable sin nombre de tipo T y devuelve su dirección, la cual, como indicamos, es un puntero a T, o, diciéndolo de otra forma, un valor de tipo *T



package main

import "fmt"

func main() {
    p := new(int) // p, de tipo *int, apunta a una variable anónima de tipo int
    fmt.Println(*p) // imprime 0
    *p = 2 // Asigna el valor 2 a la variable anónima
    fmt.Println(*p) // imprime 2
}


playground

Las variables anónimas creadas con new no son distintas a las variables comunes y silvestres de toda la vida, con la diferencia de que no es necesario darles un nombre


¿Y para qué?

Todo esto está muy bien, pero, ¿De qué me sirve? ¿Por qué no usar las variables como lo hemos hecho toda la vida si es más simple?

Los punteros nos ayudan entre otras cosas a acceder a elementos a los cuales normalmente no tendríamos accesos. Imaginemos que quiere escribir una función que intercambie el contenido de dos variables enteras. La primera aproximación podría ser algo como:


  
package main

import "fmt"

func DaSwap(i1 int, i2 int) {
	i1, i2 = i2, i1
}

func main() {
	var a int = 42
	var b int = 16

	DaSwap(a, b)

	fmt.Println(a, " ", b)
}
  

playground

¿Que imprime el programa anterior? Si esperaba que imprimiera 16 42 lamento decirle que espere sentado. Como el intercambio de las variables se hace en la función DaSwap, este se hizo sobre copias de los valores de las variables que le fueron pasadas como argumentos, por lo que estas no fueron afectadas. Si usted es un veterano de las trincheras, entonces de seguro ya está pensando en la frase paso de variables por valor.

Si quisiéramos ver que el intercambio se realice correctamente, nos vemos obligados a usar punteros:


  

package main

import "fmt"

func DaSwap(i1 *int, i2 *int) {
	*i1, *i2 = *i2, *i1
}

func main() {
	var a int = 42
	var b int = 16

	DaSwap(&a, &b)

	fmt.Println(a, " ", b)
}
  

playground

Si de verdad completó el Tour de Go, usted podría debatirme que no necesita punteros, pues podría escribir la función de tal manera que devuelva los valores como una tupla en orden inverso al pasado como argumento, ¡Y yo me vería forzado a darle la razón!

  
package main

import "fmt"

func DaSwap(i1 int, i2 int) (int, int) {
	return i2, i1
}

func main() {
	var a int = 42
	var b int = 16

	a, b = DaSwap(a, b)

	fmt.Println(a, " ", b)
}

  

playground

Entonces, otra vez ¿De que me sirven los punteros? En muchos lenguajes de programación los punteros se usan, a veces sin que nos demos cuenta, por ejemplo para implementar listas y tipos de datos especiales que requieren aumento o disminución dinámica de capacidad; y para manipular objetos y arreglos. También permiten pasar funciones como argumento a otras funciones, pues estando definido un bloque de código en una posición de memoria (o en una ubicación del stack), parece lógico poder acceder a él a través de una referencia a dicha posición de memoria.



  
# Pseudo código

function HolaSoyUnCallback() {
  # Realizo alguna acción
}

func f1(callback) {
  # Realizo alguna acción en f1
  callback()
  # Mas acciones en f1
} 
 
  


Específicamente en Go, entre otras cosas, los punteros nos dan acceso a una característica muy poderosa del lenguaje, sobre la cual se pueden construir muchas cosas muy bonitas; los structs



Resumiendo

  • Un puntero se llama así porqué sirve para apuntar
  • ¿Donde apunta? Apunta a una dirección de memoria

12/03/2022

Usted no necesita un curso de Go


Al menos no al principio de su viaje de aprendizaje.
Como con toda tecnología seria, puede aprender las bases en un fin de semana, pero necesitará una vida de esfuerzo y aprendizaje continuo para dominarla.

Si escribimos en el buscador de internet preferido de Pedro, Juan y Diego la frase Go course, aparecen de inmediato una miríada de resultados donde se ofrecen talleres, especializaciones, guías completas de cero a héroe; tanto gratuitas como de pago; prometiendo desde introducirlo en el, hasta convertirlo en un maestro en este lenguaje. No se ustedes pero yo desconfío de forma natural en cualquier cosa que prometa convertirme en ninja-wizard-valar-ponga aquí su término preferido para denotar maestría técnica, en un par de horas.

La verdad no necesita pagar para aprender Go, sus mantenedores y comunidad han preparado todo lo que necesita para conseguir una competencia técnica básica, y escribo básica porqué como con toda tecnología seria, puede aprender las bases en un fin de semana, pero necesitará una vida de esfuerzo y aprendizaje continuo para sentirse seguro y competente al usarla.

Una vez que domine las bases del lenguaje, por supuesto necesitará recursos para aprender y profundizar en técnicas avanzadas e implementaciones de ideas. Es posible que necesite un buen libro sobre construir micro servicios con Go, otro sobre como escribir apis, o sobre como implementar un repositorio para desacoplar sus fuentes de datos. Pero si lo piensa, estos recursos que sin duda necesitará tratan más sobre una tecnología o patrón que sobre el lenguaje en sí mismo.

Así que no tire su dinero en cursos que le venden algo que es gratuito y ya está a su disposición. Y si quiere saberlo, lo único que necesita para comenzar su viaje con Go es un computador conectado a internet, un navegador decente, ir al sitio del Tour de Go y seguir el tutorial interactivo que le enseñará desde declarar variables hasta como manejar concurrencia.

En serio, insisto de la forma más respetuosa posible. ¿Quiere aprender Go? No gaste dinero en cursos y abra en su navegador la página del Tour.