Translate

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

miércoles, 27 de septiembre de 2023

Libros gratuitos

 Me llegaron ests libros de web code geeks :

Get Schooled By Web Code Geeks

Download FREE IT Guides!

 

Starting with Kotlin Cheatsheet

Welcome to the Kotlin Cheatsheet! This document aims to provide you with a quick reference guide to Kotlin programming language. Whether you are a beginner getting started with Kotlin or...

 
 

Querying Graphs with Neo4j Cheatsheet

This cheatsheet is your guide to effectively querying graphs using Neo4j. Whether you’re a seasoned database professional looking to expand your skills or a curious enthusiast eager to...

 
 

Getting Started with GraphQL Cheatsheet

In this cheatsheet, we will embark on a journey to explore the core principles of GraphQL and its ecosystem. We'll cover topics such as schema design, querying and mutation operations,...

 
 

Starting with Windows PowerShell Cheatsheet

This cheatsheet provides an overview of the most commonly used PowerShell commands, grouped by category. Whether you’re new to PowerShell or an experienced user, this cheatsheet will...

 
 

Core Python Cheatsheet

This cheatsheet is intended to serve as a quick reference guide for Python programming. It covers some of the most commonly used syntax and features of the language, including data types,...

 
 

Starting with Docker Cheatsheet

This guide aims to provide an overview of the key concepts and features of Docker, along with practical examples and tips for using it effectively. It covers topics such as Docker...