Aprende Go en 10 minutos
Go (también conocido como Golang) es un lenguaje de programación compilado y tipado estáticamente diseñado en Google. Es conocido por su simplicidad, eficiencia y excelente soporte para concurrencia. Este tutorial te ayudará a aprender programación en Go rápidamente.
1. Escribiendo tu primer programa en Go
Comencemos con un programa simple. Crea un archivo llamado hello.go
e ingresa el siguiente código:
package main
import "fmt"
func main() {
fmt.Println("¡Hola, Mundo!")
}
Guarda el archivo y ejecuta el siguiente comando en la terminal:
go run hello.go
La salida será:
¡Hola, Mundo!
Este programa simple demuestra la estructura básica de Go:
package main
declara el nombre del paqueteimport "fmt"
importa el paquete de formato para operaciones de E/Sfunc main()
es el punto de entrada del programafmt.Println()
imprime texto en la consola
2. Sintaxis básica
Go tiene una sintaxis limpia y simple. A diferencia de Python, Go usa llaves {}
para definir bloques de código y requiere punto y coma al final de las declaraciones (aunque generalmente se insertan automáticamente).
// Este es un comentario de una línea
fmt.Println("¡Hola, Mundo!")
/*
Este es un comentario de múltiples líneas
que abarca varias líneas
*/
Reglas básicas de sintaxis en Go:
- Bloques de código: Definidos por llaves
{}
- Comentarios: Los comentarios de una línea comienzan con
//
, los de múltiples líneas con/* */
- Declaraciones: Terminan con punto y coma (insertado automáticamente)
- Declaración de paquete: Cada archivo comienza con una declaración de paquete
3. Variables y tipos de datos
Go es tipado estáticamente, lo que significa que debes declarar los tipos de variables. Sin embargo, Go admite inferencia de tipos con el operador :=
.
Métodos de declaración de variables:
// Declaración de tipo explícita
var nombre string = "Juan"
var edad int = 25
// Inferencia de tipos
nombre := "Juan"
edad := 25
// Declaración múltiple de variables
var x, y int = 10, 20
x, y := 10, 20
Tipos de datos básicos principales de Go:
- Tipos enteros:
int
,int8
,int16
,int32
,int64
,uint
,uint8
,uint16
,uint32
,uint64
,uintptr
- Tipos flotantes:
float32
,float64
- Cadena:
string
- Booleano:
bool
- Tipos complejos:
complex64
,complex128
3.1 Tipos numéricos
Go proporciona varios tipos numéricos para diferentes casos de uso:
// Tipos enteros
var edad int = 25
var numeroPequeño int8 = 127
var numeroGrande int64 = 9223372036854775807
// Tipos flotantes
var temperatura float32 = 36.5
var pi float64 = 3.14159265359
// Números complejos
var numeroComplejo complex64 = 3 + 4i
3.2 Tipo cadena
Las cadenas en Go son secuencias de bytes y son inmutables:
// Declaración de cadena
var saludo string = "¡Hola, Go!"
nombre := "Alicia"
// Operaciones con cadenas
fmt.Println(len(saludo)) // Longitud de la cadena
fmt.Println(saludo[0]) // Acceder al primer carácter (byte)
fmt.Println(saludo[0:5]) // Segmentación de cadena
fmt.Println(strings.ToUpper(nombre)) // Convertir a mayúsculas
3.3 Tipo booleano
El tipo booleano tiene dos valores: true
y false
:
var estaActivo bool = true
var estaCompletado bool = false
// Operaciones booleanas
resultado1 := true && false // false
resultado2 := true || false // true
resultado3 := !true // false
4. Constantes
Las constantes se declaran usando la palabra clave const
y no se pueden cambiar:
const Pi = 3.14159
const MaxUsuarios = 1000
// Múltiples constantes
const (
EstadoOK = 200
EstadoNoEncontrado = 404
EstadoError = 500
)
// Constantes tipadas
const Version string = "1.0.0"
5. Estructuras de datos
Go proporciona varias estructuras de datos integradas para almacenar y manipular datos.
5.1 Arreglos
Los arreglos son secuencias de tamaño fijo de elementos del mismo tipo:
// Declaración de arreglo
var numeros [5]int = [5]int{1, 2, 3, 4, 5}
nombres := [3]string{"Alicia", "Bob", "Carlos"}
// Accediendo a elementos
fmt.Println(numeros[0]) // 1
numeros[0] = 10 // Modificar elemento
// Longitud del arreglo
fmt.Println(len(numeros)) // 5
5.2 Slices
Los slices son arreglos dinámicos que pueden crecer y reducirse:
// Declaración de slice
numeros := []int{1, 2, 3, 4, 5}
var sliceVacio []int
// Creando slices desde arreglos
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // [2, 3, 4]
// Operaciones con slices
numeros = append(numeros, 6) // Agregar elemento
numeros = append(numeros, 7, 8, 9) // Agregar múltiples elementos
// Capacidad y longitud del slice
fmt.Println(len(numeros)) // Longitud: 9
fmt.Println(cap(numeros)) // Capacidad: 10 (puede variar)
5.3 Mapas
Los mapas son colecciones desordenadas de pares clave-valor:
// Declaración de mapa
estudiante := map[string]interface{}{
"nombre": "Juan",
"edad": 20,
"carrera": "Ciencias de la Computación",
}
// Declaración alternativa
var puntajes map[string]int = make(map[string]int)
puntajes["matemáticas"] = 95
puntajes["ciencias"] = 88
// Accediendo y modificando
fmt.Println(estudiante["nombre"])
estudiante["edad"] = 21
estudiante["promedio"] = 3.8
// Acceso seguro
if telefono, existe := estudiante["teléfono"]; existe {
fmt.Println(telefono)
} else {
fmt.Println("Teléfono no proporcionado")
}
// Iterando sobre el mapa
for clave, valor := range estudiante {
fmt.Printf("%s: %v\n", clave, valor)
}
5.4 Structs
Los structs son colecciones de campos que pueden tener diferentes tipos:
// Definición de struct
type Persona struct {
Nombre string
Edad int
Ciudad string
}
// Creando instancias de struct
persona1 := Persona{"Alicia", 25, "Nueva York"}
persona2 := Persona{
Nombre: "Bob",
Edad: 30,
Ciudad: "Londres",
}
// Accediendo a campos
fmt.Println(persona1.Nombre)
persona1.Edad = 26
6. Operaciones y operadores
Go proporciona un conjunto completo de operadores para varios cálculos y comparaciones.
- Operadores aritméticos:
+
,-
,*
,/
,%
(módulo) - Operadores de comparación:
==
,!=
,>
,<
,>=
,<=
- Operadores lógicos:
&&
,||
,!
- Operadores bit a bit:
&
,|
,^
,<<
,>>
- Operadores de asignación:
=
,+=
,-=
,*=
,/=
6.1 Operadores aritméticos
a, b := 10, 3
fmt.Printf("Suma: %d\n", a + b) // 13
fmt.Printf("Resta: %d\n", a - b) // 7
fmt.Printf("Multiplicación: %d\n", a * b) // 30
fmt.Printf("División: %d\n", a / b) // 3
fmt.Printf("Módulo: %d\n", a % b) // 1
6.2 Operadores de comparación
x, y := 5, 10
fmt.Printf("Igual: %t\n", x == y) // false
fmt.Printf("No igual: %t\n", x != y) // true
fmt.Printf("Mayor que: %t\n", x > y) // false
fmt.Printf("Menor que: %t\n", x < y) // true
6.3 Operadores lógicos
a, b := true, false
fmt.Printf("Operación AND: %t\n", a && b) // false
fmt.Printf("Operación OR: %t\n", a || b) // true
fmt.Printf("Operación NOT: %t\n", !a) // false
7. Control de flujo
Go proporciona varias declaraciones de control de flujo para gestionar la ejecución del programa.
7.1 Declaraciones if
edad := 20
if edad >= 18 {
fmt.Println("Adulto")
} else if edad >= 13 {
fmt.Println("Adolescente")
} else {
fmt.Println("Niño")
}
// if con declaración corta
if puntaje := 85; puntaje >= 90 {
fmt.Println("Calificación: A")
} else if puntaje >= 80 {
fmt.Println("Calificación: B")
} else {
fmt.Println("Calificación: C")
}
7.2 Bucles for
Go tiene solo una construcción de bucle: for
// Bucle for básico
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// Bucle estilo while
contador := 0
for contador < 5 {
fmt.Println(contador)
contador++
}
// Bucle infinito
for {
fmt.Println("Esto se ejecutará para siempre")
break // Usar break para salir
}
// Bucle range (para slices, arreglos, mapas)
frutas := []string{"manzana", "banana", "cereza"}
for indice, fruta := range frutas {
fmt.Printf("%d: %s\n", indice, fruta)
}
7.3 Declaraciones switch
dia := "Lunes"
switch dia {
case "Lunes":
fmt.Println("Inicio de la semana")
case "Viernes":
fmt.Println("El fin de semana está cerca")
case "Sábado", "Domingo":
fmt.Println("¡Fin de semana!")
default:
fmt.Println("Día regular")
}
// Switch sin expresión
puntaje := 85
switch {
case puntaje >= 90:
fmt.Println("Calificación: A")
case puntaje >= 80:
fmt.Println("Calificación: B")
case puntaje >= 70:
fmt.Println("Calificación: C")
default:
fmt.Println("Calificación: F")
}
8. Funciones
Las funciones en Go son ciudadanos de primera clase y admiten múltiples valores de retorno.
8.1 Funciones básicas
func saludar(nombre string) string {
return "¡Hola, " + nombre + "!"
}
// Llamando la función
mensaje := saludar("Juan")
fmt.Println(mensaje)
8.2 Múltiples valores de retorno
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("no se puede dividir por cero")
}
return a / b, nil
}
// Usando múltiples valores de retorno
resultado, err := dividir(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Resultado:", resultado)
}
8.3 Valores de retorno nombrados
func calcularRectangulo(ancho, altura float64) (area float64, perimetro float64) {
area = ancho * altura
perimetro = 2 * (ancho + altura)
return // retorno desnudo
}
area, perimetro := calcularRectangulo(5, 3)
fmt.Printf("Área: %.2f, Perímetro: %.2f\n", area, perimetro)
8.4 Funciones variádicas
func sumar(numeros ...int) int {
total := 0
for _, num := range numeros {
total += num
}
return total
}
fmt.Println(sumar(1, 2, 3, 4)) // 10
fmt.Println(sumar(5, 10, 15)) // 30
9. Punteros
Go tiene punteros pero con una sintaxis más simple que C/C++:
func modificarValor(x *int) {
*x = *x * 2
}
func main() {
valor := 10
fmt.Println("Antes:", valor) // 10
modificarValor(&valor)
fmt.Println("Después:", valor) // 20
}
10. Métodos
Los métodos son funciones con un argumento receptor:
type Rectangulo struct {
Ancho float64
Altura float64
}
// Método con receptor de valor
func (r Rectangulo) Area() float64 {
return r.Ancho * r.Altura
}
// Método con receptor de puntero
func (r *Rectangulo) Escalar(factor float64) {
r.Ancho *= factor
r.Altura *= factor
}
rect := Rectangulo{Ancho: 5, Altura: 3}
fmt.Println("Área:", rect.Area()) // 15
rect.Escalar(2)
fmt.Println("Área escalada:", rect.Area()) // 60
11. Interfaces
Las interfaces definen firmas de métodos que los tipos pueden implementar:
type Figura interface {
Area() float64
Perimetro() float64
}
type Circulo struct {
Radio float64
}
func (c Circulo) Area() float64 {
return 3.14159 * c.Radio * c.Radio
}
func (c Circulo) Perimetro() float64 {
return 2 * 3.14159 * c.Radio
}
func imprimirInfoFigura(f Figura) {
fmt.Printf("Área: %.2f, Perímetro: %.2f\n", f.Area(), f.Perimetro())
}
circulo := Circulo{Radio: 5}
imprimirInfoFigura(circulo)
12. Manejo de errores
Go usa manejo explícito de errores en lugar de excepciones:
func leerArchivo(nombreArchivo string) (string, error) {
datos, err := os.ReadFile(nombreArchivo)
if err != nil {
return "", fmt.Errorf("falló al leer el archivo %s: %w", nombreArchivo, err)
}
return string(datos), nil
}
contenido, err := leerArchivo("ejemplo.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Contenido:", contenido)
13. Concurrencia con Goroutines
Las goroutines son hilos ligeros administrados por el runtime de Go:
func trabajador(id int) {
for i := 0; i < 3; i++ {
fmt.Printf("Trabajador %d: %d\n", id, i)
time.Sleep(time.Millisecond * 100)
}
}
func main() {
// Iniciar múltiples goroutines
for i := 1; i <= 3; i++ {
go trabajador(i)
}
// Esperar a que las goroutines completen
time.Sleep(time.Second)
fmt.Println("Todos los trabajadores completaron")
}
14. Canales
Los canales se utilizan para la comunicación entre goroutines:
func productor(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i // Enviar valor al canal
time.Sleep(time.Millisecond * 100)
}
close(ch) // Cerrar canal cuando termine
}
func consumidor(ch <-chan int) {
for valor := range ch {
fmt.Println("Recibido:", valor)
}
}
func main() {
ch := make(chan int, 3) // Canal con buffer
go productor(ch)
consumidor(ch)
fmt.Println("Comunicación por canal completada")
}
15. Operaciones con archivos
Go proporciona métodos simples para leer y escribir archivos:
// Leyendo archivos
datos, err := os.ReadFile("ejemplo.txt")
if err != nil {
fmt.Println("Error leyendo archivo:", err)
return
}
fmt.Println("Contenido del archivo:", string(datos))
// Escribiendo archivos
contenido := "¡Hola, Go!\n"
err = os.WriteFile("salida.txt", []byte(contenido), 0644)
if err != nil {
fmt.Println("Error escribiendo archivo:", err)
return
}
fmt.Println("Archivo escrito exitosamente")
16. Paquetes y módulos
Los módulos de Go gestionan dependencias y versiones de paquetes:
// Importando paquetes de la biblioteca estándar
import (
"fmt"
"math"
"strings"
)
func main() {
fmt.Println(math.Sqrt(16)) // 4
fmt.Println(strings.ToUpper("go")) // GO
}
Para crear tu propio paquete, crea un directorio con el nombre de tu paquete y exporta funciones capitalizando sus nombres.
17. Pruebas
Go tiene soporte integrado para pruebas:
// En el archivo math_test.go
package main
import "testing"
func TestSumar(t *testing.T) {
resultado := sumar(2, 3)
esperado := 5
if resultado != esperado {
t.Errorf("sumar(2, 3) = %d; se esperaba %d", resultado, esperado)
}
}
func sumar(a, b int) int {
return a + b
}
Ejecuta las pruebas con: go test
18. Mejores prácticas
- Usa
gofmt
para formatear tu código - Sigue las convenciones de nomenclatura de Go (camelCase para variables, PascalCase para exportaciones)
- Maneja errores explícitamente
- Usa interfaces para abstracción
- Prefiere composición sobre herencia
- Escribe pruebas completas
- Usa la biblioteca estándar siempre que sea posible
Este tutorial cubre las características esenciales de la programación en Go. Con práctica, podrás construir aplicaciones eficientes y concurrentes usando las poderosas características de Go.