Read the book: «Programación en Go»

Font:



Programación en Go

Primera edición, 2021

© 2021 Mario Macías Lloret

© 2021 MARCOMBO, S. L.

www.marcombo.com

Diseño de cubierta: ENEDENÚ DISEÑO GRÁFICO

Maquetación: Reverte-Aguilar

Correctora: Anna Alberola

Directora de producción: M.a Rosa Castillo Producción del ePub: booqlab

Todos los logotipos utilizados en este libro son propiedad de sus respectivas empresas y su uso en este libro es meramente didáctico:

Ilustración de cubierta: Renee French - Creative Commons Attribution 3.0 licensed

Logotipo GO de cubierta: Equipo de Go (www.blog.golang.org/go-brand) - Creative Commons Attribution 3.0 licensed

Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra.

ISBN: 978-84-267-3249-1

Este libro está dedicado a Isabel. Sin tus ideas y ánimos, tu paciencia y generosidad, estas palabras nunca habrían sido posibles.

TABLA DE CONTENIDO

INTRODUCCIÓN

Acerca de este libro

Organización del libro

Convenciones de formato

Acerca del autor

CAPÍTULO 1. INSTALACIÓN Y USO DE GO

1.1 Instalando Go

1.2 Comandos básicos de Go

1.3 Editando su código en Go

1.4 Compilando y ejecutando su primer programa

CAPÍTULO 2. SINTAXIS BÁSICA DE GO

2.1 Tipos de datos básicos

2.2 Cadenas de texto. El tipo string

2.3 Definición de variables

2.4 Conversiones explícitas de tipos

2.5 Constantes

2.6 Bases de numeración

2.7 Operadores numéricos

2.8 Operadores numéricos de comparación

2.9 Operadores aplicables al tipo string

2.10 Operadores lógicos con bool

2.11 Operadores lógicos a nivel de bit

2.12 Salida estándar de datos

2.13 Entrada estándar de datos

CAPÍTULO 3. CONTROL DE FLUJO

3.1 Bloques condicionales

3.1.1 if

3.1.2 if ... else

3.1.3 switch - case

3.2 Órdenes iterativas (bucles for)

3.3 Contexto y ocultación de variables

CAPÍTULO 4. APUNTADORES

4.1 Definición de un apuntador

4.2 La referencia a nil

4.3 Apuntando hacia una variable

4.4 Leyendo o modificando el valor apuntado

4.5 Valores versus referencias

CAPÍTULO 5. FUNCIONES

5.1 Definición e invocación

5.2 Retorno de valores

5.3 Retorno de múltiples valores

5.4 Retorno de múltiples valores nombrados

5.5 El identificador vacío

5.6 Paso por valor vs. paso por referencia

5.7 Literales de función

5.8 Otras consideraciones

CAPÍTULO 6. ESTRUCTURAS DE DATOS LINEALES

6.1 Vectores

6.2 Porciones

6.3 Declarando variables a porciones

6.4 Añadir elementos a una porción. Función append

6.5 Medir dimensiones con len y cap

6.6 Controlar el tamaño inicial con make

6.7 Copia de porciones con copy

6.8 Uso de porciones en funciones

6.9 Recorriendo vectores y porciones

6.10 Creando “vistas” desde las porciones

6.11 Funciones con número variable de argumentos

6.12 El operador difusor

CAPÍTULO 7. CADENAS DE TEXTO

7.1 Diferencias con porciones y vectores

7.2 Obteniendo la longitud de un string

7.3 De string a porción

7.4 Construcción dinámica de cadenas

7.4.1 Concatenación de cadenas

7.4.2 Construcción con strings.Builder

7.4.3 Paquete fmt

CAPÍTULO 8. DICCIONARIOS (MAPAS)

8.1 Declaración de mapas

8.2 Acceso a elementos

8.3 Eliminando entradas con delete

8.4 Recorriendo mapas con range

8.5 Contando el número de elementos

8.6 Conjuntos

8.7 Detalles internos de map

CAPÍTULO 9. ORGANIZACIÓN DE CÓDIGO

Paquetes y módulos

9.1 Paquetes (package)

9.2 Módulos

9.3 Creando módulos y paquetes

9.4 Importando paquetes del módulo local

9.4.1 Dependencias circulares

9.5 Incorporando paquetes de módulos externos

9.6 Copias locales de módulos. El directorio vendor

9.7 Elementos públicos y privados a nivel de paquete

9.8 Alias de paquete

9.9 La función init

CAPÍTULO 10. DEFINICIÓN DE TIPOS DE DATOS

10.1 Tipos a partir de porciones

10.2 Tipos a partir de mapas

10.3 Tipos funcionales

10.4 Receptores de función. Métodos

10.5 Tipos pseudoenumerados

10.5.1 El operador iota

10.6 Caso de estudio: time.Duration

CAPÍTULO 11. TIPOS DE DATOS ESTRUCTURADOS

Struct

11.1 Tipos de datos estructurados: struct

11.2 Punteros a struct

11.3 Receptores de función y creación de métodos

11.4 Incrustado de estructuras

11.5 La estructura vacía: struct{}

11.6 Caso práctico: opciones funcionales como alternativa a constructores

CAPÍTULO 12. INTERFACES

12.1 Caso de estudio: la interfaz Stringer

12.2 La filosofía del tipado estructural

12.3 Implementando interfaces: receptores ¿mediante apuntadores o mediante valores?

12.4 La interfaz vacía interface{}

12.5 Manejo seguro de tipos de datos

12.6 Incrustando interfaces

CAPÍTULO 13. GESTIÓN DE ERRORES

13.1 La interfaz error

13.2 Instanciando errores de manera genérica

13.3 Comprobación de tipos de error

13.3.1 Errores centinela

13.3.2 Distintas implementaciones de error

13.4 Envolviendo errores

13.5 Verificando la cadena de errores: errors.As

13.6 defer

13.7 Entrando en pánico

13.8 Función panic

13.9 Función recover

CAPÍTULO 14. ENTRADA Y SALIDA

Flujos de datos

14.1 Interfaces io.Writer e io.Reader

14.2 Archivos de disco

14.3 Entrada y salida formateada

14.4 Paquete bufio

14.5 Paquete ioutil

CAPÍTULO 15. PARALELISMO Y CONCURRENCIA

Gorrutinas

15.1 Un poco de historia

15.2 Gorrutinas

15.3 Sincronización mediante sync.WaitGroup

15.4 Problemas de concurrencia: condiciones de carrera

15.5 Sincronización mediante sync.Mutex

15.5.1 sync.RWMutex

15.6 Sincronización mediante atomic

15.7 Conclusiones: ¿cuándo y cómo sincronizar gorrutinas?

CAPÍTULO 16. CANALES

16.1 Creación, uso y cierre

16.2 Canales solo de lectura y de escritura

16.3 Bloqueo en la escritura: canales con o sin búfer

16.4 Iterando canales con for

16.5 Múltiples receptores

16.6 Sincronización mediante canales

16.7 Demultiplexión con select

16.8 Cancelando lecturas después de un tiempo de espera

16.9 Cancelando tareas mediante contextos

CAPÍTULO 17. SERVICIOS WEB

17.1 HTTP explicado en 3 minutos

17.2 REST explicado en 3 minutos

17.3 Creación de un servicio HTTP en Go

17.3.1 Interfaz http.Handler

17.3.2 Funciones http.ListenAndServe y http.ListenAndServeTLS

17.3.3 Ejemplo de servidor HTTP

17.4 Creación de un cliente HTTP en Go

17.4.1 Ejemplo de cliente HTTP

17.5 Ejemplo práctico de servicio REST

17.5.1 Probando el servicio REST

CAPÍTULO 18. SERIALIZACIÓN DE DATOS

18.1 Serialización de tipos Go a JSON

18.2 Deserialización de JSON a tipos Go

18.3 Serializando y deserializando documentos JSON sin formato

18.4 Serialización de porciones y arrays

18.5 Serialización y deserialización en otros formatos

CAPÍTULO 19. CONEXIÓN A BASES DE DATOS SQL

19.1 Carga de controlador

19.2 Abriendo una base de datos

19.3 Modificando la base de datos

19.4 Consultando datos

19.5 Declaraciones preparadas

19.6 Transacciones

19.7 Reserva de conexiones

CAPÍTULO 20. PRUEBAS AUTOMATIZADAS DE SOFTWARE

20.1 Código a probar: la función Factorial

20.2 El paquete testing

20.3 Probando servicios HTTP

20.4 Pruebas de rendimiento

20.5 Cobertura de las pruebas

INTRODUCCIÓN

Go es el lenguaje de moda entre informáticos. Sin duda, es uno de los lenguajes de programación que durante la última década han ganado más impulso entre los programadores de diversas disciplinas. Pese a ser un lenguaje relativamente “joven”, no ha tardado en hacerse con una legión, ya no de adeptos, sino de auténticos fans.

Son muchas las causas del éxito de Go:

• Es versátil. Combina características de los lenguajes de bajo nivel, como C++, con características de lenguajes dinámicos, como Python o Ruby. Esto hace de Go un lenguaje tan idóneo para software de sistema (controladores, comandos de sistema, agentes de monitorización, incluso programación de sistemas embebidos gracias a la implementación de tinygo.org) como para la creación de grandes aplicaciones web y sistemas de servicios distribuidos.

• Es rápido. Es un lenguaje cuyos ejecutables se distribuyen en código nativo, sin necesidad de máquinas virtuales o intérpretes de lenguaje. Compila las técnicas de optimización más vanguardistas.

• Es compacto. Genera ejecutables pequeños que incluyen todo el código necesario, sin necesidad de bibliotecas externas o entornos de ejecución extra.

• Es muy rápido compilando. Está enfocado hacia las tendencias actuales de integración y despliegue continuo de aplicaciones, mediante las cuales el software está en continua actualización. Hoy, el tiempo de compilación es una métrica clave para la productividad de los equipos de desarrollo.

• Es seguro. A diferencia de otros lenguajes como C o C++, donde un apuntador a memoria “desbocado” puede suponer un grave fallo de seguridad, Go comprueba la seguridad de los accesos a memoria de tal manera que un usuario malintencionado lo tendrá mucho más difícil para encontrar fallos de seguridad explotables.

• Es sencillo. Los equipos de desarrollo modernos pasan muchas horas revisando código de sus compañeros, con tal de reforzar unos estándares de calidad altos. Go es un lenguaje diseñado para ser fácil de leer y entender, lo cual incentiva unas prácticas y estilos globales y unificados, y evita proveer múltiples soluciones para una misma tarea.

• Es completo. La distribución estándar de Go proporciona casi todas las herramientas que un profesional necesita: gestores de dependencias, analizadores de rendimiento, formateadores y analizadores de código, depuradores, gestión de la documentación, una enorme biblioteca estándar de funcionalidades, etc.

• Es código abierto. El código de todas las herramientas oficiales de Go, así como su librería estándar, es abierto y libre de modificar y distribuir. Además, sus bibliotecas de terceros también son código abierto.

ACERCA DE ESTE LIBRO

Este libro pretende ser un punto tanto de contacto como de profundización en el lenguaje de programación Go. Está destinado tanto a personas con conocimientos básicos de programación como a profesionales con experiencia que quieran adentrarse en los paradigmas y filosofía de un nuevo lenguaje.

En ningún caso es una introducción a la programación para personas que nunca hayan programado, ni un curso de algoritmia básica. No obstante, muchos de los conceptos que se presentan se explican brevemente, de manera que todo el mundo pueda entenderlos.

Si usted es un programador experto, podrá sacar buen provecho de este libro, ya que no se limita a explicar las estructuras básicas de programación adaptadas a la sintaxis de Go, sino también su filosofía y los nuevos conceptos que hacen de Go un lenguaje único y especial.

Este libro no pretende ser un manual de referencia técnico, ni un compendio de todas las bibliotecas y funciones estándar de Go. Para ese cometido ya existe la documentación oficial. Este libro pretende ser una introducción ágil —sin descuidar la profundización— a las herramientas y características que le permitirán escribir programas en Go de manera productiva, en un breve periodo de tiempo.

Como autor, humildemente —pero no por ello sin ambición—, pretendo que este libro sea la herramienta que a mí me hubiera gustado tener para agilizar mi transición profesional de programador en C y Java hacia el lenguaje Go.

ORGANIZACIÓN DEL LIBRO

Los 20 capítulos de este libro se agrupan en cuatro partes diferenciadas.

La primera parte comprende los capítulos del 1 al 9, y muestra los constructos esenciales de Go, comunes a casi cualquier otro lenguaje de programación imperativo y estructurado. Los programadores expertos serán capaces de leer y asimilar de manera rápida las particularidades de la sintaxis de Go, mediante breves explicaciones y ejemplos concisos. Los programadores menos iniciados encontrarán explicaciones sencillas a muchos conceptos que puedan ser nuevos para ellos.

La segunda parte comprende los capítulos del 10 al 14. Los conceptos aquí explicados tienen sus equivalentes en otros lenguajes de programación, aunque en Go se abordan desde otro paradigma, que cambiará la manera en que diseñamos nuestro software respecto a cuando lo hacemos para lenguajes más clásicos.

Durante el transcurso de los capítulos del 10 al 14, el lector podrá empezar a intuir por qué Go es un lenguaje “diferente”, y cómo aúna filosofías que se pensaban irreconciliables, al situarse entre los lenguajes de programación de bajo nivel y los lenguajes interpretados y dinámicos.

La tercera parte está compuesta por los capítulos 15 y 16, en los que el lector entrará de lleno en los conceptos y herramientas que hacen de Go un lenguaje único para la computación de altas prestaciones, tanto por la potencia de sus herramientas como por su casi insultante sencillez. El lector aprenderá a lanzar miles de tareas en paralelo, a coordinarlas y a establecer una comunicación sencilla y efectiva entre estas.

La cuarta parte está enfocada a la programación de aplicaciones en Go. Además de su lenguaje, su filosofía y sus detalles, este libro pretende servir también como punto de contacto con la programación efectiva de aplicaciones. Por ello, la cuarta y última parte de este libro muestra, paso a paso, cómo utilizar estas funcionalidades de la biblioteca estándar de Go que rápidamente nos permitirán empezar a programar un amplio abanico de aplicaciones.

El capítulo 17 está dedicado a la creación de servicios web en Go: cómo entablar la comunicación entre programas situados remotamente. El capítulo 18 muestra cómo serializar estructuras de datos complejas en Go a formatos de texto, para su intercambio a través de Internet o su guardado en disco, por ejemplo. El capítulo 19 explica las funciones básicas que permitirán conectar nuestras aplicaciones en Go a bases de datos relacionales, para el guardado persistente de datos estructurados y relacionados. Por último, el capítulo 20 muestra el sistema de Go para llevar a cabo una de las prácticas actualmente esenciales durante el desarrollo y mantenimiento de aplicaciones: las pruebas de código automatizadas (testing).

CONVENCIONES DE FORMATO

Este libro intercala texto explicativo con extractos de código, así como con capturas de sesiones interactivas de terminal.

El código fuente se muestra en una fuente de ancho fijo, con algunas palabras clave de Go resaltadas. Ejemplo:

for { var i = 3 funcion(i) }

Para una mejor visualización del código en el formato de un libro, las líneas de código no ocuparán más de 60 caracteres de ancho, y las indentaciones/tabulaciones ocuparán 2 caracteres.

La entrada y salida de datos a través del terminal de línea de comandos se muestra en fuente de ancho fijo y sin resaltado. Los comandos que el usuario escribe desde el terminal están precedidos del símbolo del dólar, $, a semejanza de las líneas de comando Unix/Linux:

$ go run hola.go Esto es lo que muestra el programa: ¡Hola!

En algunos momentos, se muestran plantillas que describen de manera genérica algunas partes de la sintaxis de Go. Las partes entre símbolos < y > deben substituirse por un texto que represente el concepto explicado, sin dichos símbolos. Las partes entre símbolos [ y ] representan partes opcionales, que pueden omitirse.

Por ejemplo, la plantilla

var <nombre> <tipo> [ = valor ]

podría coincidir con cualquiera de las siguientes líneas válidas de Go:

var i int var nombre string var pi float32 = 3.1416

Por brevedad, en los ejemplos de código se omiten algunas partes que serían necesarias en un programa completo, cuando estas no aportan información interesante al ejemplo o a la explicación, tales como:

• Cabeceras de la función principal: func main()

• Definición de paquetes: package main

• Importación de paquetes externos: import "net/http"

ACERCA DEL AUTOR

Nacido durante las últimas “hornadas” de la generación baby boom, tuve la suerte de entablar contacto con la informática desde pequeñito gracias a mi tío Antonio, que nos consiguió un micro-ordenador Sony MSX. Cuando fui capaz de aprender programación en el lenguaje BASIC de nuestro MSX, supe que quería dedicarme profesionalmente a ello. Creo que fue gracias a la determinación surgida de este fortísimo deseo que conseguí acabar, con mucha frustración y dificultad, mis estudios de secundaria. Los odiaba, pero me permitieron llegar a estudiar Ingeniería Informática en la universidad, una de las mejores cosas que me han pasado en la vida.

Después de trabajar unos años en empresas, volví al mundo académico para trabajar y realizar mi doctorado en Arquitectura de Computadores en el Barcelona Supercomputing Center y en la Universitat Politécnica de Catalunya donde, además, tuve el privilegio de dar clases de programación durante 10 años. Al poco tiempo de volver al mundo empresarial, entré en contacto con Go, nada más entrar en la empresa de monitorización New Relic (newrelic.com).

He pretendido aunar en este libro mi pasión por la programación y mi pasión por la docencia. Solo me queda agradecerle su lectura, y esperar que lo disfrute tanto como yo disfruté escribiéndolo.