/cien-dias-go

Cien días de código en Go 💯🐭

Primary LanguageGo

Cien días de código en Golang

💯🐭

Los nombres de los proyectos no reflejan los días. Sólo siguen su propia secuencia. Están ordenados de más reciente a más antiguo.

Sorting

Para ordernar tipos de datos hay que implementar la interfaz de sort.Sort() que consta de tres métodos

Len() int

Less(i, j int) bool

Swap(i, j int)

Once

Las struct pueden contener propiedades anónimas que agregan una composición de datos.

type Point struct {
	X, Y float64	
}

type ColoredPoint struct {
	Point
	color string
}

Para construir estas estructuras se usan puros valores

color := "red"
c := geometry.ColoredPoint{ geometry.Point{1, 1}, color }

Packages

Alias para el paquete

import (
    "crypto/rand"
    mrand "math/rand" // alternative name mrand avoids conflict
)

Si no se usa el paquete que se importa Go marca un error. Para evitarlo se puede importar en "blanco"

import _ "image/png" // register PNG decoder

Cada vez que se invoca un método de un tipo o estructura se realiza una copia de la información. Para evitarlo se puede pasar un apuntador

func (p *Point) ScaleBy(factor float64) {
    p.X *= factor
    p.Y *= factor
}

La convención es que si hay un método que recibe un apuntador entonces todos los métodos deben recibir un apuntador. Se invoca de la siguiente manera

// La simple, haciendo uso de la transformación implícita del compilador
p.ScaleBy(2) // funciona sólo para variables

// OTRAS FORMAS

r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"

// --
p := Point{1, 2}
pptr := &p
pptr.ScaleBy(2)
fmt.Println(p) // "{2, 4}"

// --
p := Point{1, 2}
(&p).ScaleBy(2)
fmt.Println(p) // "{2, 4}"

Nueve

Go no tiene en estos momentos un buen soporte de genéricos. Por ejemplo no es posible definir una estructura que tenga un valor genérico que permita hacer comparaciones con todos los operadores: ==, !=, <, > <=, >=.

La interfaz comparable sólo opera con == y !=.

Para poder crear una estructura que pueda soportar un valor con múltiples tipos se puede declarar una interfaz.

type Valuable interface {
	int | float64 | string
}

Posteriormente se usa dicha interfaz para declarar una struct usando la misma sintaxis como en generic.

type BinaryTree[T Valuable] struct {
	value		T
	left,right	*BinaryTree[T]
}

Ocho

Si los archivos .go de una carpeta no corresponden todos al mismo package hay errores.

Para organizarlo se pueden crear subcarpetas, cada una con archivos que declaran un package diferente.

Si no hay un package main no se puede ejecutar usando go run .. Esto significa que paquetes diferentes a main sólo pueden ser librerías.

Es posible sobrecargar la representación de un tipo mediante una función, por ejemplo aquí se hace con string

func (f Farenheit) String() string {
	return fmt.Sprintf("%.2f °F", f)
}

func (c Celsius) String() string {
	return fmt.Sprintf("%.2f °C", c)
}

De la misma manera

func (f Farenheit) IntoCelsius() Celsius {
	return Celsius( (f - 32) / 1.8000 )
}

func (k Kelvin) IntoCelsius() Celsius {
	return Celsius(k - 273.15)
}

Para usar la sobrecarga

temp := Celsius(37)
conv := temp.IntoFarenheit()
convStr = conv.String()

Siete

Declarar constantes que asemejan a enums

type EventType string

const (
	one		EventType = "one"
	two				  = "two"		
	three			  = "three"
)

type EventTypeNumber uint8

const (
	oneNumber	EventTypeNumber = iota
	twoNumber
	threeNumber
)

iota genera autonumeración comenzando en cero.

Seis

Siguiendo https://www.mongodb.com/languages/golang y https://www.mongodb.com/blog/post/quick-start-golang--mongodb--modeling-documents-with-go-data-structures

Cinco

Declaraciones de estructuras son

type nombre struct {
    ...
}

Los campos van dentro de la declaración indicando su tipo de datos

type person struct {
    ID string
}

Las estructuras soportan anotaciones para los campos usando ``, por ejemplo para convertir a un campo JSON

type person struct {
    ID string `json:"id"` 
}

Declarar variables con una estructura como tipo se da de la forma

var onePerson = person{ ID: "1" }

var persons = person[]{
    {ID: "1"},
    {ID: "2"},
}

El paquete gin es un web framework. Opcionalmente se necesita el paquete net/http para ciertas constantes estándares en HTTP.

import (
	"net/http"
	"github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    router.Run("localhost:8080")
}

""" gin.Context is the most important part of Gin. It carries request details, validates and serializes JSON, and more. (Despite the similar name, this is different from Go’s built-in context package.) """

Las funciones que realizan acciones reciben un apuntador a gin.Context.

func getAlbums(c *gin.Context) {
	c.IndentedJSON(http.StatusOK, albums)
}

func main() {
	router := gin.Default()
	router.GET("/albums", getAlbums)
	router.Run("localhost:8080")
}

Gin también puede transformar los datos enviados en una nueva entidad a partir de la struct.

Para esto usa c.BindJSON()

func postAlbum(c *gin.Context) {
	var newAlbum album
	if err := c.BindJSON(&newAlbum); err != nil {
		return ;
	}

	albums = append(albums, newAlbum)
	c.IndentedJSON(http.StatusCreated, newAlbum)
}

Se observa que cuando se envían datos vacíos la entidad se crea con datos por default.

...
  {
    "id": "",
    "title": "",
    "artist": "",
    "price": 0
  }
...

También que gin envía automáticamente un error 400 si falla el parse y no se manipula la respuesta del servidor.

Una respuesta puede enviar un objeto a partir de la struct H de gin.

c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})

Remover un elemento en un slice se realiza mendiante la composición de los elementos excluyentes

    for i, album := range albums {
		if album.ID == id {
			albums = append(albums[:i], albums[i+1:]...)
			c.Status(200)
			return
		}
	}

Cuatro

Go tiene manipulación de imágenes en la librería estandar mediante el paquete img.

import "img"

Para abrir una imágen es necesario primero crear un io.Reader

reader, err := os.Open(filePath)
img, s, err := image.Decode(reader)

En este caso s es el formato de la imagen (png, jpeg, gif), img contiene una estructura con referencia a la imagen.

Es posible cargar una librería sólo por sus efectos secundarios mediante import _, ejemplos:

import _ "image/png"
import _ "image/jpeg"

// juntos

import (
    _ "image/png"
    _ "image/jpeg"
)

En este caso después de importar es posible operar con imágenes png y jpeg.

El paquete os también permite leer archivos, aunque en este caso retorna un reader

err, reader := os.Open(filePath)

Usar defer permite detener la ejecución de una función hasta que todas las llamadas o manipulaciones bajo ella hayan sido completadas

reader, err := os.Open(filePath)
if err != nil {
	fmt.Printf("Error reading file %s: %v", filePath, err)
	os.Exit(1)
}
	
defer reader.Close()

Para separar funcionalidades en diferentes archivos dentro del mismo paquete sólo hace falta declarar el mismo en los diferentes archivos

// main.go
package main
...


// filters.go
package main

El paquete image tiene colores en el rango [0, 65535]. La explicación está en el blog del paquete. Para usarlo es necesario convertilo a uint8 ejemplo ru8 := uint8(r / 0x101).

Para escribir una imagen a archivo es necesario utilizar un descriptor y un codificador dependiendo del tipo de imagen

    if imgFormat == "png" {
		saveErr := png.Encode(writer, img)
		if saveErr != nil {
			writer.Close()
			return false;
		}
	}

Tres

El paquete io/ioutil soporta carga de archivos

import "io/ioutil"

func main() {
    ...
    data, err := ioutil.ReadFile(path)
}

data es un arreglo de byte, para convertirlo en string

...
dataString := string(data)
...

Uno

El paquete os tiene un slice, Args, que almacena los argumentos con que el programa fue invocado

import (
    "fmt"
    "os"
)
...

for index, arg := range os.Args {
    fmt.Println(index, arg);
}

El paquete strconv contiene funciones para hacer conversiones de strings

// val == 0, err != nil
val, err := strconv.Atoi("x")


// val == 10, err == nil
val, err := strconv.Atoi("10")

Para hacer un join the strings se puede usar el paquete strings

strings.Join(os.Args[1:], " ")

El estilo recomendado para nombrar en Go es usar camelCase para los identificadores, excepto los que son de acceso público, que utilizan PascalCase.

Se pueden declarar constantes usando const. Sólo pueden ser number, string o boolean