Translate

miércoles, 11 de octubre de 2023

Estructuras de Control en Go


Las estructuras de control de Go están relacionadas con las de C pero difieren en aspectos importantes. No hay bucle do o while, sólo un for ligeramente generalizado; switch es más flexible; if y switch aceptan una declaración de inicialización opcional como la de for; las declaraciones  break y continue toman una etiqueta opcional para identificar qué interrumpir o continuar; y hay nuevas estructuras de control que incluyen un interruptor tipo y un multiplexor de comunicaciones multidireccional, select. La sintaxis también es ligeramente diferente: no hay paréntesis y los cuerpos siempre deben estar delimitados por llaves.


En Go, un if simple se ve así:


if x > 0 {

    return y

}


Las llaves obligatorias alientan a escribir declaraciones if simples en varias líneas. Es un buen estilo hacerlo de todos modos, especialmente cuando el cuerpo contiene una declaración de control como return o break.

Dado que if y switch aceptan una declaración de inicialización, es común ver una utilizada para configurar una variable local.


if err := file.Chmod(0664); err != nil {

    log.Print(err)

    return err

}


En las bibliotecas Go, encontrará que cuando una declaración if no fluye hacia la siguiente declaración (es decir, el cuerpo termina en break, continue, goto o return), se omite el else innecesario.


f, err := os.Open(name)

if err != nil {

    return err

}

codeUsing(f)


Este es un ejemplo de una situación común en la que el código debe protegerse contra una secuencia de condiciones de error. El código se lee bien si el flujo de control exitoso recorre la página, eliminando los casos de error a medida que surgen. Dado que los casos de error tienden a terminar en declaraciones de devolución, el código resultante no necesita else.


f, err := os.Open(name)

if err != nil {

    return err

}

d, err := f.Stat()

if err != nil {

    f.Close()

    return err

}

codeUsing(f, d)


El último ejemplo demuestra un detalle, cómo funciona las declaración corta :=. La declaración que llama a os.Open dice:


f, err := os.Open(name)


Esta declaración declara dos variables, f y err. Unas líneas más tarde, la llamada a f.Stat dice:


d, err := f.Stat()


que parece como si declarara d y err. Observe, sin embargo, que err aparece en ambas afirmaciones. Esta duplicación es legal: err se declara en la primera declaración, pero solo se reasigna en la segunda. Esto significa que la llamada a f.Stat utiliza la variable err existente declarada anteriormente y simplemente le da un nuevo valor.

En una declaración := puede aparecer una variable v incluso si ya ha sido declarada, siempre que:

  • esta declaración está en el mismo alcance que la declaración existente de v (si v ya está declarado en un alcance externo, la declaración creará una nueva variable),
  • el valor correspondiente en la inicialización es asignable a v, y
  • hay al menos otra variable creada por la declaración.

Esta propiedad inusual es puro pragmatismo, lo que facilita el uso de un único valor de error, por ejemplo, en una larga cadena if-else. Verás que se usa con frecuencia.

Vale la pena señalar aquí que en Go el alcance de los parámetros de la función y los valores de retorno es el mismo que el del cuerpo de la función, aunque aparecen léxicamente fuera de las llaves que encierran el cuerpo.

El bucle for es similar, pero no igual, al de C. Unifica for ,while y do-while. Hay tres formas, de las cuales sólo una tiene punto y coma.


// Like a C for

for init; condition; post { }


// Like a C while

for condition { }


// Like a C for(;;)

for { }


Las declaraciones breves facilitan la declaración de la variable de índice directamente en el bucle.


sum := 0

for i := 0; i < 10; i++ {

    sum += i

}


Si está realizando un bucle sobre una matriz, un segmento, una cadena o un mapa, o leyendo desde un canal, una cláusula de rango puede gestionar el bucle.


for key, value := range oldMap {

    newMap[key] = value

}


Si solo necesita el primer elemento del rango (la clave o índice), elimine el segundo:


for key := range m {

    if key.expired() {

        delete(m, key)

    }

}


Si solo necesita el segundo elemento del rango (el valor), utilice el identificador en blanco, un guión bajo, para descartar el primero:


sum := 0

for _, value := range array {

    sum += value

}


Para las cadenas, range , desglosa en caracteres código Unicode individuales analizando el UTF-8. Las codificaciones erróneas consumen un byte y producen la runa de reemplazo U+FFFD.


for pos, char := range "日本\x80語" { // \x80 is an illegal UTF-8 encoding

    fmt.Printf("character %#U starts at byte position %d\n", char, pos)

}


esto imprime: 


character U+65E5 '日' starts at byte position 0

character U+672C '本' starts at byte position 3

character U+FFFD '�' starts at byte position 6

character U+8A9E '語' starts at byte position 7


Finalmente, Go no tiene operador de coma y ++ y -- son declaraciones, no expresiones. Por lo tanto, si desea ejecutar múltiples variables en un for, debe usar la asignación paralela.


// Reverse a

for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {

    a[i], a[j] = a[j], a[i]

}



El switch de Go es más general que el de C. No es necesario que las expresiones sean constantes o incluso números enteros, los casos se evalúan de arriba a abajo hasta que se encuentra una coincidencia y, si el interruptor no tiene expresión, se activa como verdadero. Por lo tanto, es posible (e idiomático) escribir una cadena if-else-if-else como un switch.


func unhex(c byte) byte {

    switch {

    case '0' <= c && c <= '9':

        return c - '0'

    case 'a' <= c && c <= 'f':

        return c - 'a' + 10

    case 'A' <= c && c <= 'F':

        return c - 'A' + 10

    }

    return 0

}


No hay clasificación automática, pero los casos se pueden presentar en listas separadas por comas.


func shouldEscape(c byte) bool {

    switch c {

    case ' ', '?', '&', '=', '#', '+', '%':

        return true

    }

    return false

}


Aunque no son tan comunes en Go como en otros lenguajes similares a C, las declaraciones de break se pueden utilizar para finalizar un caso. A veces, sin embargo, es necesario salir de un bucle circundante, no del interruptor, y en Go eso se puede lograr colocando una etiqueta en el bucle y "rompiendo" esa etiqueta. Este ejemplo muestra ambos usos.


Loop:

    for n := 0; n < len(src); n += size {

        switch {

        case src[n] < sizeOne:

            if validateOnly {

                break

            }

            size = 1

            update(src[n])


        case src[n] < sizeTwo:

            if n+1 >= len(src) {

                err = errShortInput

                break Loop

            }

            if validateOnly {

                break

            }

            size = 2

            update(src[n] + src[n+1]<<shift)

        }

    }


Por supuesto, la instrucción continue también acepta una etiqueta opcional pero se aplica sólo a los bucles.


Aquí hay una rutina de comparación para segmentos de bytes que utiliza dos declaraciones de cambio:


// Comparar devuelve un número entero que compara los dos segmentos de bytes,

// lexicográficamente.

// El resultado será 0 si a == b, -1 si a < b, y +1 si a > b

func Compare(a, b []byte) int {

    for i := 0; i < len(a) && i < len(b); i++ {

        switch {

        case a[i] > b[i]:

            return 1

        case a[i] < b[i]:

            return -1

        }

    }

    switch {

    case len(a) > len(b):

        return 1

    case len(a) < len(b):

        return -1

    }

    return 0

}


También se puede utilizar un modificador para descubrir el tipo dinámico de una variable de interfaz. Un cambio de tipo de este tipo utiliza la sintaxis de una aserción de tipo con la palabra clave tipo entre paréntesis. Si el modificador declara una variable en la expresión, la variable tendrá el tipo correspondiente en cada cláusula. También es idiomático reutilizar el nombre en tales casos, declarando de hecho una nueva variable con el mismo nombre pero de un tipo diferente en cada caso.


var t interface{}

t = functionOfSomeType()

switch t := t.(type) {

default:

    fmt.Printf("unexpected type %T\n", t)     // %T prints whatever type t has

case bool:

    fmt.Printf("boolean %t\n", t)             // t has type bool

case int:

    fmt.Printf("integer %d\n", t)             // t has type int

case *bool:

    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool

case *int:

    fmt.Printf("pointer to integer %d\n", *t) // t has type *int

}

Traits en Rust


Rust te permite abstraer tipos con rasgos o traits. Son similares a las interfaces de java o c#:

struct Dog { name: String, age: i8 }

struct Cat { lives: i8 } // No name needed, cats won't respond anyway.


trait Pet {

    fn talk(&self) -> String;

}


impl Pet for Dog {

    fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) }

}


impl Pet for Cat {

    fn talk(&self) -> String { String::from("Miau!") }

}


fn greet<P: Pet>(pet: &P) {

    println!("Oh you're a cutie! What's your name? {}", pet.talk());

}


fn main() {

    let captain_floof = Cat { lives: 9 };

    let fido = Dog { name: String::from("Fido"), age: 5 };


    greet(&captain_floof);

    greet(&fido);

}


Los objetos de rasgo permiten valores de diferentes tipos, por ejemplo en una colección:


fn main() {

    let pets: Vec<Box<dyn Pet>> = vec![

        Box::new(Cat { lives: 9 }),

        Box::new(Dog { name: String::from("Fido"), age: 5 }),

    ];

    for pet in pets {

        println!("Hello, who are you? {}", pet.talk());

    }

}


Disposición de la memoria después de asignar mascotas:



Los tipos que implementan un Trait determinado pueden ser de diferentes tamaños. Esto hace que sea imposible tener cosas como Vec<dyn Pet> en el ejemplo anterior.

dyn Pet es una forma de informarle al compilador acerca de un tipo de tamaño dinámico que implementa Pet.

En el ejemplo, las mascotas se asignan en la pila y los datos vectoriales están en el montón. Los dos elementos del vector son fat pointers o punteros gordos:

Un fat pointers es un puntero de doble ancho. Tiene dos componentes: un puntero al objeto real y un puntero a la tabla de métodos virtuales (vtable) para la implementación Pet de ese objeto en particular.

Los datos del perro llamado Fido son los campos de nombre y edad. El Gato tiene un campo de vida.

martes, 10 de octubre de 2023

Transformación digital en América Latina

 

Monomorfización en Rust


El código genérico se convierte en código no genérico según los sitios de llamadas:


fn main() {

    let integer = Some(5);

    let float = Some(5.0);

}

se comporta como si escribieras


enum Option_i32 {

    Some(i32),

    None,

}


enum Option_f64 {

    Some(f64),

    None,

}


fn main() {

    let integer = Option_i32::Some(5);

    let float = Option_f64::Some(5.0);

}


Esta es una abstracción de costo cero: obtienes exactamente el mismo resultado que si hubieras codificado manualmente las estructuras de datos sin la abstracción.


sábado, 7 de octubre de 2023

bbolt: Una Base de Datos Embebida para Go


Cuando se trata de almacenar y gestionar datos en una aplicación Go, una de las opciones más populares y eficientes es bbolt. bbolt es una base de datos embebida de código abierto diseñada específicamente para aplicaciones Go. 

Pero, ¿Qué es bbolt? bbolt, también conocida como "BoltDB", es una base de datos embebida escrita en Go que se centra en la simplicidad, el rendimiento y la eficiencia en la utilización de recursos. A diferencia de las bases de datos tradicionales que se ejecutan como procesos separados, bbolt se integra directamente en la aplicación Go, lo que la convierte en una excelente opción para aplicaciones que necesitan un almacenamiento local y ligero sin la necesidad de un servidor de base de datos independiente.

Características Clave de bbolt son: 

  • Transacciones ACID: bbolt garantiza la integridad de los datos mediante transacciones ACID (Atomicidad, Consistencia, Aislamiento y Durabilidad). Esto significa que las operaciones de lectura y escritura se realizan de manera segura y confiable.
  • Soporte para Índices:Puedes crear índices para acelerar las consultas de datos, lo que es especialmente útil cuando necesitas recuperar datos de manera eficiente en función de ciertos criterios.
  • Eficiencia en el Uso de la Memoria: bbolt está diseñada para utilizar la memoria de manera eficiente, lo que la hace adecuada para aplicaciones con recursos limitados.
  • Almacenamiento Duradero: Los datos almacenados en bbolt son persistentes y duraderos. Puedes confiar en que los datos sobrevivirán incluso después de reiniciar la aplicación.


Para comenzar a usar bbolt en tu proyecto Go, primero debemos agregar la dependencia:


go get go.etcd.io/bbolt@latest


Vamos a importar bbolt : 


import bolt "go.etcd.io/bbolt"


Luego, podemos abrir una base de datos o crear una nueva:


db, err := bolt.Open(path, 0600, nil)

if err != nil {

  return err

}

defer db.Close()


A partir de ahí, puedes crear buckets (similares a tablas en una base de datos relacional), almacenar y recuperar datos, y gestionar transacciones de manera similar a otras bases de datos. El enfoque es simple y eficiente, lo que facilita el trabajo con la base de datos.


// Abre una transacción de escritura.

tx, err := db.Begin(true)

if err != nil {

    log.Fatal(err)

}

defer tx.Rollback()


// Obtiene un bucket (o lo crea si no existe).

bucket, err := tx.CreateBucketIfNotExists([]byte("mi-bucket"))

if err != nil {

    log.Fatal(err)

}


// Almacena un valor en el bucket.

err = bucket.Put([]byte("clave"), []byte("valor"))

if err != nil {

    log.Fatal(err)

}


// Realiza commit de la transacción.

if err := tx.Commit(); err != nil {

    log.Fatal(err)

}


// Recupera un valor del bucket.

valor := bucket.Get([]byte("clave"))

fmt.Printf("Valor recuperado: %s\n", valor)


bbolt es una opción atractiva para aplicaciones Go que necesitan una base de datos embebida rápida, eficiente y fácil de usar. Su diseño simple, soporte para transacciones ACID y eficiencia en el uso de recursos la convierten en una elección sólida para muchas aplicaciones. Podemos considerar a bbolt para tus proyectos Go que requieran almacenamiento de datos local y duradero.


Dejo link: 

https://pkg.go.dev/go.etcd.io/bbolt

https://github.com/etcd-io/bbolt

viernes, 6 de octubre de 2023

Punto y coma en Go


Al igual que C, la gramática formal de Go usa punto y coma para terminar declaraciones, pero a diferencia de C, esos punto y coma no aparecen en la fuente. En cambio, Lexer utiliza una regla simple para insertar puntos y coma automáticamente mientras escanea, por lo que el texto de entrada está prácticamente libre de ellos.

La regla es esta. Si el último token antes de una nueva línea es un identificador (que incluye palabras como int y float64), un literal básico como un número o una cadena constante, o uno de los tokens


break continue fallthrough return ++ -- ) }


el lexer siempre inserta un punto y coma después del token. Esto podría resumirse como "si la nueva línea viene después de un token que podría finalizar una declaración, inserte un punto y coma".

También se puede omitir un punto y coma inmediatamente antes de una llave de cierre, por lo que una declaración como


     go func() { for { dst <- <-src } }()


no necesita punto y coma. Los programas idiomáticos de Go tienen punto y coma solo en lugares como cláusulas de bucle for, para separar los elementos inicializador, condición y continuación. También son necesarios para separar varias declaraciones en una línea, en caso de que escriba el código de esa manera.

Una consecuencia de las reglas de inserción de punto y coma es que no se puede colocar la llave de apertura de una estructura de control (if, for, switch o select) en la línea siguiente. Si lo hace, se insertará un punto y coma antes del aparato ortopédico, lo que podría provocar efectos no deseados. 


if i < f() {

    g()

}


así no


if i < f()  // wrong!

{           // wrong!

    g()

}

jueves, 5 de octubre de 2023

Generics en Rust


Rust soporta genéricos, lo que le permite abstraer algoritmos o estructuras de datos (como ordenamiento o un árbol binario) sobre los tipos definidos por nosotros o por el sdk.

Puede utilizar genéricos para abstraer el tipo de campo concreto:

#[derive(Debug)]

struct Point<T> {

    x: T,

    y: T,

}


fn main() {

    let integer = Point { x: 5, y: 10 };

    let float = Point { x: 1.0, y: 4.0 };

    println!("{integer:?} and {float:?}");

}


Lo hermoso es que infiere el tipo dependiendo de como lo usamos. Es decir, si hacemos : 


let x = Point { x: 5, y: 10.0 };


no va a compilar. 


Se puedes declarar un tipo genérico en tu bloque impl:


#[derive(Debug)]

struct Point<T>(T, T);


impl<T> Point<T> {

    fn x(&self) -> &T {

        &self.0  // + 10

    }


    // fn set_x(&mut self, x: T)

}


fn main() {

    let p = Point(5, 10);

    println!("p.x = {}", p.x());

}


¿Por qué T se especifica dos veces en impl<T> Point<T> {}? ¿No es eso redundante?

Esto se debe a que es una sección de implementación genérica para tipos genéricos. Son genéricos independientemente. Significa que estos métodos están definidos para cualquier T.

Es posible escribir impl Point<u32> { .. }.

Point sigue siendo genérico y puedes usar Point<f64>, pero los métodos de este bloque solo estarán disponibles para Point<u32>.

miércoles, 4 de octubre de 2023

Agregando Swagger a nuestro proyecto Echo


Luego de crear nuestro proyecto con echo, vamos a agregar swagger

Empezamos agregando la librería de swag 


go get -d github.com/swaggo/swag/cmd/swag

go install github.com/swaggo/swag/cmd/swag@latest


Luego ejecutamos Swag en la carpeta raíz del proyecto Go que contiene el archivo main.go, Swag analizará los comentarios y generará los archivos necesarios (carpeta docs y docs/doc.go).


swag init


y luego agregamos echo para swagger


go get -u github.com/swaggo/echo-swagger


Y ahora tenemos swagger en nustro proyecto echo : 


package main


import (

"github.com/labstack/echo/v4"

"github.com/swaggo/echo-swagger"


_ "github.com/swaggo/echo-swagger/example/docs" // docs is generated by Swag CLI, you have to import it.

)


// @title Swagger Example API

// @version 1.0

// @description This is a sample server Petstore server.

// @termsOfService http://swagger.io/terms/


// @contact.name API Support

// @contact.url http://www.swagger.io/support

// @contact.email support@swagger.io


// @license.name Apache 2.0

// @license.url http://www.apache.org/licenses/LICENSE-2.0.html


// @host petstore.swagger.io

// @BasePath /v2

func main() {

e := echo.New()


e.GET("/swagger/*", echoSwagger.WrapHandler)


e.Logger.Fatal(e.Start(":1323"))

}


En mi caso solo tengo que agregar : 

el import : 

import (_ "myapp/docs")

y el router : 

e.GET("/swagger/*", echoSwagger.WrapHandler)

y tengo estos 2 servicios con su documentación  : 

// HealthCheck godoc
// @Summary Show the status of server.
// @Description get the status of server.
// @Tags root
// @Accept */*
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /health/check [get]
func HealthCheck(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]interface{}{
"data": "Server is up and running",
})
}

// Info godoc
// @Summary Show the info of server.
// @Description get the info of server.
// @Tags root
// @Accept */*
// @Produce json
// @Success 200 {object} computer.SysInfo
// @Router /health/info [get]
func Info(c echo.Context) error {
return c.JSON(http.StatusOK, computer.Info())
}

Y se genero el siguiente swagger.json :

{
    "swagger": "2.0",
    "info": {
        "contact": {}
    },
    "paths": {
        "/health/check": {
            "get": {
                "description": "get the status of server.",
                "consumes": [
                    "*/*"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "root"
                ],
                "summary": "Show the status of server.",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/health/info": {
            "get": {
                "description": "get the info of server.",
                "consumes": [
                    "*/*"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "root"
                ],
                "summary": "Show the info of server.",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/computer.SysInfo"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "computer.SysInfo": {
            "type": "object",
            "properties": {
                "disk": {
                    "type": "integer"
                },
                "hostname": {
                    "type": "string"
                },
                "platform": {
                    "type": "string"
                },
                "ram": {
                    "type": "integer"
                },
                "ram available": {
                    "type": "integer"
                },
                "ram free": {
                    "type": "integer"
                },
                "uptime": {
                    "type": "integer"
                }
            }
        }
    }
}

Dejo link : https://github.com/swaggo/echo-swagger

martes, 3 de octubre de 2023

Módulos en Rust y su Ubicación


Rust es conocido por su sistema de módulos sólido y su capacidad para administrar y organizar el código de manera efectiva. Los módulos permiten dividir un proyecto en partes más pequeñas y lógicas, facilitando el mantenimiento y la colaboración.

Los módulos en Rust son unidades de organización para el código que ayudan a dividir una aplicación en partes más pequeñas y manejables. Estos módulos permiten agrupar funciones, tipos, estructuras y otros elementos relacionados de manera lógica.

Para definir un módulo en Rust, utilizamos la palabra clave mod. Por ejemplo:

// Definición de un módulo llamado 'my_module'

mod my_module {

    // Contenido del módulo aquí

}

En Rust, la ubicación de los módulos dentro de un proyecto es importante para su visibilidad y accesibilidad desde otros módulos. 

Omitir mod le indicará a Rust que lo busque en otro archivo, si escribimos:

mod garden;

Esto le indica a Rust que el contenido del módulo garden se encuentra en src/garden.rs. De manera similar, se puede encontrar un módulo garden::vegetables en src/garden/vegetables.rs.

La el directorio raíz está en:

  • src/lib.rs (para una libreria)
  • src/main.rs (para una aplicación binaria)
Los módulos definidos en archivos también se pueden documentar mediante "comentarios internos del documento". Estos documentan el elemento que los contiene (en este caso, un módulo).

//! This module implements the garden, including a highly performant germination
//! implementation.

// Re-export types from this module.
pub use seeds::SeedPacket;
pub use garden::Garden;

/// Sow the given seed packets.
pub fn sow(seeds: Vec<SeedPacket>) { todo!() }

/// Harvest the produce in the garden that is ready.
pub fn harvest(garden: &mut Garden) { todo!() }


Antes de Rust 2018, los módulos debían estar ubicados en module/mod.rs en lugar de module.rs, y esta sigue siendo una alternativa funcional para las ediciones posteriores a 2018.

La razón principal para introducir filename.rs como alternativa a filename/mod.rs fue porque muchos archivos llamados mod.rs pueden ser difíciles de distinguir en los IDE.

Un anidamiento más profundo puede utilizar carpetas, incluso si el módulo principal es un archivo:

src/
├── main.rs
├── top_module.rs
└── top_module/
    └── sub_module.rs

El lugar donde Rust buscará los módulos se puede cambiar con una directiva del compilador:

#[path = "some/path.rs"]
mod some_module;

Esto es útil, por ejemplo, si desea colocar pruebas para un módulo en un archivo llamado some_module_test.rs, similar a la convención en Go.


Paths en Rust




Los Paths se resuelven de la siguiente manera:

  • Como ruta relativa:
    • foo o self::foo se refiere a foo en el módulo actual,
    • super::foo se refiere a foo en el módulo principal.
  • Como Path absoluto:
    • crate::foo se refiere a foo en la raíz del modulo actual,
    • bar::foo se refiere a foo en el modulo de bar. 

Un módulo puede incorporar símbolos de otro módulo al alcance con su uso. Normalmente verá algo como esto en la parte superior de cada módulo:

use std::collections::HashSet;

use std::mem::transmute;

lunes, 2 de octubre de 2023

Crear un proyecto en Go y Echo


Vamos a crear un proyecto con echo y go. Echo es un framework web minimalista y de alto rendimiento para Go. Está diseñado para ser simple y eficiente, lo que lo hace adecuado para el desarrollo rápido de API REST. Echo es especialmente apreciado por su rendimiento y facilidad de uso.


$ mkdir myapp && cd myapp

$ go mod init myapp

$ go get github.com/labstack/echo/v4

$ go get github.com/labstack/echo/v4/middleware


Con esto, hemos importado las librerías necesarias si miramos el archivo go.mod : 


module myapp 


go 1.19


require (

github.com/golang-jwt/jwt v3.2.2+incompatible // indirect

github.com/labstack/echo/v4 v4.11.1 // indirect

github.com/labstack/gommon v0.4.0 // indirect

github.com/mattn/go-colorable v0.1.13 // indirect

github.com/mattn/go-isatty v0.0.19 // indirect

github.com/valyala/bytebufferpool v1.0.0 // indirect

github.com/valyala/fasttemplate v1.2.2 // indirect

golang.org/x/crypto v0.11.0 // indirect

golang.org/x/net v0.12.0 // indirect

golang.org/x/sys v0.10.0 // indirect

golang.org/x/text v0.11.0 // indirect

golang.org/x/time v0.3.0 // indirect

)


Ahora vamos a hacer un pequeño programa que tenga un hola mundo : 


import (

  "github.com/labstack/echo/v4"

  "github.com/labstack/echo/v4/middleware"

  "net/http"

)


func main() {

  // Echo instance

  e := echo.New()


  // Middleware

  e.Use(middleware.Logger())

  e.Use(middleware.Recover())


  // Routes

  e.GET("/", hello)


  // Start server

  e.Logger.Fatal(e.Start(":1323"))

}


// Handler

func hello(c echo.Context) error {

  return c.String(http.StatusOK, "Hola Mundo!")

}


Y Listo!!


Dejo links: 

https://echo.labstack.com/docs

https://github.com/labstack/echo

domingo, 1 de octubre de 2023

Zig: Un Lenguaje de Programación Moderno y Seguro


Zig es un lenguaje de programación diseñado para ofrecer una combinación de alto rendimiento, seguridad y facilidad de uso. A diferencia de muchos otros lenguajes, Zig se centra en proporcionar un control más preciso sobre el hardware y una sintaxis clara y legible. Y permite interacción con C o C++

Una de las características más notables de Zig es su enfoque en la "compilación asegurada". Esto significa que Zig se esfuerza por evitar comportamientos indefinidos en tiempo de ejecución y errores comunes en tiempo de compilación. El compilador de Zig es riguroso en la detección de errores y problemas de seguridad, lo que facilita la creación de software más confiable y seguro desde el principio.

Zig se enorgullece de su sintaxis clara y legible, lo que facilita la lectura y escritura de código. La intención del código es evidente, lo que ayuda a reducir la posibilidad de errores y facilita la colaboración entre equipos de desarrollo. Veamos un ejemplo : 


const std = @import("std");


pub fn main() void {

    const stdout = std.io.getStdOut().writer();

    try stdout.print("¡Hola, mundo!\n");

}


A pesar de su sintaxis amigable, Zig brinda un control preciso sobre el hardware y el sistema operativo. Esto lo hace adecuado para programación de sistemas, desarrollo de controladores, y otras tareas de bajo nivel.

Zig es compatible con diversas plataformas, incluidas Windows, macOS, Linux y más. Esto permite a los desarrolladores escribir código una vez y ejecutarlo en múltiples sistemas operativos sin modificarlo significativamente.

Zig es un lenguaje de programación moderno y seguro que combina la facilidad de uso con un alto nivel de control y rendimiento. Si estás interesado en la creación de software confiable y eficiente, Zig podría ser una excelente opción para explorar.


Dejo link : https://ziglang.org/

Nombres en Go


Los nombres son tan importantes en Go como en cualquier otro Lenguaje. Incluso tienen un efecto semántico: la visibilidad de un nombre fuera de un paquete está determinada por si su primer carácter está en mayúscula. Por lo tanto, vale la pena dedicar un poco de tiempo a hablar sobre las convenciones de nomenclatura en los programas Go.

Nombres de paquetes: Cuando se importa un paquete, el nombre del paquete se convierte en un descriptor de acceso para el contenido. Después de


import "bytes"


el paquete que importa puede usar bytes.Buffer. Es útil si todos los que usan el paquete pueden usar el mismo nombre para referirse a su contenido, lo que implica que el nombre del paquete debe ser bueno: breve, conciso y evocador. Por convención, los paquetes reciben nombres de una sola palabra en minúsculas; no debería haber necesidad de guiones bajos ni mayúsculas mixtas. Peque por el lado de la brevedad, ya que todos los que usen su paquete escribirán ese nombre. Y no te preocupes por las colisiones a priori. El nombre del paquete es sólo el nombre predeterminado para las importaciones; No es necesario que sea único en todo el código fuente y, en el raro caso de una colisión, el paquete importado puede elegir un nombre diferente para usarlo localmente. En cualquier caso, la confusión es rara porque el nombre del archivo en la importación determina exactamente qué paquete se está utilizando.

Otra convención es que el nombre del paquete es el nombre base de su directorio fuente; el paquete en src/encoding/base64 se importa como "encoding/base64" pero tiene el nombre base64, no encoding_base64 ni encodingBase64.

El importador de un paquete usará el nombre para referirse a su contenido, por lo que los nombres exportados en el paquete pueden usar ese hecho para evitar repeticiones. (No utilice la notación import ., que puede simplificar las pruebas que deben ejecutarse fuera del paquete que están probando, pero que de lo contrario debe evitarse). Por ejemplo, el tipo Reader en el paquete bufio se llama Reader, no BufReader, porque los usuarios lo ven como bufio.Reader, que es un nombre claro y conciso. Además, debido a que las entidades importadas siempre se abordan con el nombre de su paquete, bufio.Reader no entra en conflicto con io.Reader. De manera similar, la función para crear nuevas instancias de ring.Ring, que es la definición de un constructor en Go, normalmente se llamaría NewRing, pero dado que Ring es el único tipo exportado por el paquete, y dado que el paquete se llama ring, es llamado simplemente New, que los clientes del paquete ven como ring.New. En conclusión, se debe de utilizar la estructura del paquete para ayudarle a elegir buenos nombres.

Otro ejemplo breve es once.Do; once.Do(setup) se lee bien y no mejoraría si se escribiera once.DoOrWaitUntilDone(setup). Los nombres largos no hacen que las cosas sean más legibles automáticamente. Un comentario útil sobre un documento a menudo puede ser más valioso que un nombre muy largo.

Getters: Go no proporciona soporte automático para getters y setters. No hay nada de malo en proporcionar getters y setters usted mismo, y a menudo es apropiado hacerlo, pero no es idiomático ni necesario poner Get en el nombre del getter. Si tiene un campo llamado owner, el método getter debe llamarse Owner, no GetOwner. El uso de nombres en mayúsculas para la exportación proporciona el gancho para discriminar el campo del método. Una función de set, si es necesaria, probablemente se llamará SetOwner. Ambos nombres se leen bien en la práctica:


owner := obj.Owner()

if owner != user {

    obj.SetOwner(user)

}

Nombres de interfaz: Por convención, las interfaces de un método se denominan mediante el nombre del método más un sufijo -er o una modificación similar para construir un sustantivo de agente: Reader, Writer, Formatter, CloseNotifier, etc.

Para evitar confusiones, no le dé a su método uno de esos nombres a menos que tenga la misma firma y significado. Por el contrario, si su tipo implementa un método con el mismo significado que un método de un tipo conocido, asígnele el mismo nombre y firma; por ejemplo llame a su método convertidor a string String, no ToString.

MixedCaps: la convención en Go es usar MixedCaps en lugar de guiones bajos para escribir nombres de varias palabras.


Comentarios en Go


Go proporciona comentarios de bloque /* */ estilo C y comentarios de línea // estilo C++. Los comentarios de línea son la norma; Los comentarios de bloque aparecen principalmente como comentarios de paquete, pero son útiles dentro de una expresión o para deshabilitar grandes extensiones de código.

Se considera que los comentarios que aparecen antes de las declaraciones de nivel superior, sin nuevas líneas intermedias, documentan la declaración misma. Estos "comentarios de documentos" son la documentación principal de un paquete o comando de Go determinado. 

Los “comentarios de documentos” son comentarios que aparecen inmediatamente antes de las declaraciones de paquete, const, func, type y var de nivel superior sin nuevas líneas intermedias. Cada nombre exportado (en mayúscula) debe tener un comentario de documento.

Los paquetes go/doc y go/doc/comment brindan la capacidad de extraer documentación del código fuente de Go, y una variedad de herramientas utilizan esta funcionalidad. El comando go doc busca e imprime el comentario del documento para un paquete o símbolo determinado. (Un símbolo es una constante, función, tipo o var de nivel superior). El servidor web pkg.go.dev muestra la documentación para los paquetes públicos de Go (cuando sus licencias permiten ese uso). El programa que sirve a ese sitio es golang.org/x/pkgsite/cmd/pkgsite, que también se puede ejecutar localmente para ver la documentación de módulos privados o sin conexión a Internet. El servidor de idiomas gopls proporciona documentación al editar archivos fuente de Go en IDE.

Por ejemplo, Cada paquete debe tener un comentario que lo presente. Proporciona información relevante para el paquete en su conjunto y, en general, establece expectativas para el paquete. Especialmente en paquetes grandes, puede resultar útil que el comentario del paquete brinde una breve descripción general de las partes más importantes de la API, vinculando a otros comentarios de documentos según sea necesario.

Si el paquete es simple, el comentario del paquete puede ser breve. Por ejemplo:


// Package path implements utility routines for manipulating slash-separated

// paths.

//

// The path package should only be used for paths separated by forward

// slashes, such as the paths in URLs. This package does not deal with

// Windows paths with drive letters or backslashes; to manipulate

// operating system paths, use the [path/filepath] package.

package path


Los corchetes en [ruta/ruta de archivo] crean un enlace de documentación.

Como se puede ver en este ejemplo, los comentarios de Go doc utilizan oraciones completas. Para un comentario de paquete, eso significa que la primera oración comienza con "Package".

Dejo link: https://go.dev/doc/comment



Visibilidad entre modulos en Rust


En Rust podemos definir la visibilidad de los métodos por medio de los modulos. 

Los elementos del módulo son privados de forma predeterminada (oculta los detalles de implementación).

Los elementos de padres y hermanos siempre están visibles.

En otras palabras, si un elemento es visible en el módulo foo, será visible en todos los descendientes de foo.


mod outer {

    fn private() {

        println!("outer::private");

    }


    pub fn public() {

        println!("outer::public");

    }


    mod inner {

        fn private() {

            println!("outer::inner::private");

        }


        pub fn public() {

            println!("outer::inner::public");

            super::private();

        }

    }

}


fn main() {

    outer::public();

}


Utilice la palabra clave pub para hacer públicos los medotos de los módulos.

Además, existen especificadores pub(...) avanzados para restringir el alcance de la visibilidad pública.

Configurar la visibilidad como pub(crate) es un patrón común.

Con menos frecuencia, puedes dar visibilidad a una ruta específica.

En cualquier caso, se debe otorgar visibilidad a un módulo ancestro (y a todos sus descendientes).


Dejo link: https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself