Estas estudiando Golang y queres aprender patrones de diseño? bueno, te dejo este recurso que te va a venir super bien.
Dejo link: https://github.com/tmrts/go-patterns
Estas estudiando Golang y queres aprender patrones de diseño? bueno, te dejo este recurso que te va a venir super bien.
Dejo link: https://github.com/tmrts/go-patterns
En nuestro caso de uso de ejemplo, la interfaz del servicio ProductInfo se puede definir utilizando Protocol buffers como se muestra en el siguiente ejemplo:
syntax = "proto3";
package ecommerce;
service ProductInfo {
rpc addProduct(Product) returns (ProductID);
rpc getProduct(ProductID) returns (Product);
}
message Product {
string id = 1;
string name = 2;
string description = 3;
}
message ProductID {
string value = 1;
}
La definición de servicio de ProductInfo se compone de una definición de interfaz de servicio donde especificamos los métodos remotos, sus parámetros de entrada y salida, y la definición de tipo (o formatos de mensaje) de esos parámetros.
/// Shortens a string to the given length.
///
/// ```
/// # use playground::shorten_string;
/// assert_eq!(shorten_string("Hello World", 5), "Hello");
/// assert_eq!(shorten_string("Hello World", 20), "Hello World");
/// ```
pub fn shorten_string(s: &str, length: usize) -> &str {
&s[..std::cmp::min(length, s.len())]
}
Como cada año se publica el indice tiobe que indica el uso de los lenguajes. Y este año la novedad fue el amplio crecimiento de C#.
"Por primera vez en la historia del índice TIOBE, C# ha ganado el premio al lenguaje de programación del año. ¡Felicidades! C# ha estado entre los 10 mejores durante más de 2 décadas y ahora que se está poniendo al día con los 4 grandes lenguajes, ganó el merecido premio al ser el lenguaje con el mayor repunte en un año (+1,43%). Los segundos puestos son Scratch (+0,83%) y Fortran (+0,64%). C# le está quitando cuota de mercado a Java y se está volviendo cada vez más popular en dominios como backends de aplicaciones web y juegos (gracias a Unity). C# se puede utilizar de forma gratuita y evoluciona a un ritmo constante, lo que hace que el lenguaje sea más expresivo con cada nueva versión. C# llegó para quedarse y pronto podría incluso superar a Java.
Aparte de C#, el año pasado hubo muchos cambios interesantes en el índice TIOBE. Fortran y Kotlin se convirtieron permanentemente en los 20 mejores jugadores reemplazando a los antiguos favoritos R y Perl. Fortran está muy preparado para hacer cálculos con buenas bibliotecas y sigue siendo uno de los favoritos de las universidades en muchos ámbitos. Kotlin es el competidor de Java fácil de aprender y escribir. Pregunta interesante: ¿qué lenguajes entrarán en el top 20 del índice TIOBE en 2024? Esto es muy difícil de predecir. Julia tocó brevemente el índice TIOBE en 2023, pero no pudo mantener esa posición. Se necesita la madurez del idioma y la comunidad de Julia para tener una segunda oportunidad. Apostaría por Dart (con Flutter) y TypeScript. Este último ya se utiliza mucho en la industria, pero por alguna razón todavía no se destaca en el índice TIOBE. Veamos qué nos depara el 2024." -- Paul Jansen, director ejecutivo de TIOBE Software (Esto nos dice la pagina oficial de TIOBE.)
Como lenguje más utilizado lo tenemos a python, seguido de c, c++ y java.
Jan 2024 | Jan 2023 | Change | Programming Language | Ratings | Change | |
---|---|---|---|---|---|---|
1 | 1 | Python | 13.97% | -2.39% | ||
2 | 2 | C | 11.44% | -4.81% | ||
3 | 3 | C++ | 9.96% | -2.95% | ||
4 | 4 | Java | 7.87% | -4.34% | ||
5 | 5 | C# | 7.16% | +1.43% | ||
6 | 7 | JavaScript | 2.77% | -0.11% | ||
7 | 10 | PHP | 1.79% | +0.40% | ||
8 | 6 | Visual Basic | 1.60% | -3.04% | ||
9 | 8 | SQL | 1.46% | -1.04% | ||
10 | 20 | Scratch | 1.44% | +0.86% | ||
11 | 12 | Go | 1.38% | +0.23% | ||
12 | 27 | Fortran | 1.09% | +0.64% | ||
13 | 17 | Delphi/Object Pascal | 1.09% | +0.36% | ||
14 | 15 | MATLAB | 0.97% | +0.06% | ||
15 | 9 | Assembly language | 0.92% | -0.68% | ||
16 | 11 | Swift | 0.89% | -0.31% | ||
17 | 25 | Kotlin | 0.85% | +0.37% | ||
18 | 16 | Ruby | 0.80% | +0.01% | ||
19 | 18 | Rust | 0.79% | +0.18% | ||
20 | 31 | COBOL | 0.78% | +0.45% |
La verdad es que descreo un poco de este indice, por ejemplo me llama la atención que Rust bajo, cuando en general veo que va creciendo mucho. O por ejemplo que sea más utilizado Scratch que Go o Rust. Son cositas que no me cierran :(
Dejo link: https://www.tiobe.com/tiobe-index/
Cuando desarrollamos una aplicación gRPC, lo primero que hacemos es definir una interfaz de servicio. La definición de la interfaz de servicio contiene información sobre cómo los consumidores pueden consumir el servicio, qué métodos permite que los consumidores llamen de forma remota, qué parámetros de método y formatos de mensaje usar al invocar esos métodos, etc. El lenguaje que especificamos en la definición del servicio se conoce como lenguaje de definición de interfaz (IDL).
Con esa definición de servicio, se puede generar el código del lado del servidor conocido como esqueleto del servidor, que simplifica la lógica del lado del servidor al proporcionar abstracciones de comunicación de bajo nivel. Además, puede generar el código del lado del cliente, conocido como código auxiliar del cliente, que simplifica la comunicación del lado del cliente con abstracciones para ocultar la comunicación de bajo nivel para diferentes lenguajes de programación. Los métodos que especifique en la definición de la interfaz de servicio pueden ser invocados remotamente por el lado del cliente tan fácilmente como realizar una invocación de función local. El framework gRPC subyacente maneja todas las complejidades que normalmente se asocian con el cumplimiento de contratos de servicio estrictos, serialización de datos, comunicación de red, autenticación, control de acceso, observabilidad, etc.
Para comprender los conceptos fundamentales de gRPC, echemos un vistazo a un caso de uso del mundo real de un microservicio implementado con gRPC. Supongamos que estamos creando una aplicación minorista en línea compuesta por múltiples microservicios.
La definición del servicio se especifica en el archivo ProductInfo.proto, que utilizan tanto el lado del servidor como el del cliente para generar el código. En este ejemplo, hemos asumido que el servicio se implementa utilizando el lenguaje Go y que el consumidor se implementa utilizando Java. La comunicación de red entre el servicio y el consumidor se realiza a través de HTTP/2.
Para hacerlas en Rust, debemos crear un archivo .rs en una carpeta tests/ :
// tests/my_library.rs
use my_library::init;
#[test]
fn test_init() {
assert!(init().is_ok());
}
Y listo.
Las pruebas están anotadas con el atributo #[prueba]. Las pruebas unitarias a menudo se colocan en un módulo de pruebas anidadas, usando #[cfg(test)] para compilarlas condicionalmente. Veamos un ejemplo:
fn first_word(text: &str) -> &str {
match text.find(' ') {
Some(idx) => &text[..idx],
None => &text,
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_empty() {
assert_eq!(first_word(""), "");
}
#[test]
fn test_single_word() {
assert_eq!(first_word("Hello"), "Hello");
}
#[test]
fn test_multiple_words() {
assert_eq!(first_word("Hello World"), "Hello");
}
}
Podemos correr los test con cargo test:
$ cargo test
Compiling hello_cargo v0.1.0
Finished test [unoptimized + debuginfo] target(s) in 0.37s
Running unittests src/main.rs
running 3 tests
test test::test_multiple_words ... ok
test test::test_empty ... ok
test test::test_single_word ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go: downloading google.golang.org/protobuf v1.28.1
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go: downloading google.golang.org/grpc v1.2.1
Luego escribir el .proto :
syntax = "proto3";
option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
Este archivo, es un descriptor del servicio y nos permite generar código con el comando protoc, por ejemplo:
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./greetings/greetings.proto
Y luego podemos escribir el cliente y el servidor. Por ejemplo:
Server:
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
y el cliente:
package main
import (
"context"
"flag"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
Y listo!
Para probarlo, ejecutamos el server :
$ go run greeter_server/main.go
Y luego el cliente :
$ go run greeter_client/main.go
Dejo link: https://grpc.io/docs/languages/go/quickstart/
use std::collections::HashSet;
use std::process::abort;
Paths:
Los paths se pueden resolver de la siguiente manera:
foo o self::foo se refiere a foo del módulo actual,
super::foo se refiere a foo en el módulo principal.
crate::foo se refiere a foo de la raíz del proyecto actual,
bar::foo se refiere a foo en la caja de bar.
Es común “reexportar” símbolos en una ruta más corta. Por ejemplo, las bibliotecas de nivel superior en una caja podrían tener
mod storage;
pub use storage::disk::DiskStorage;
pub use storage::network::NetworkStorage;
haciendo que DiskStorage y NetworkStorage estén disponibles para otras cajas con una ruta corta y conveniente.
En su mayor parte, sólo es necesario utilizar los elementos que aparecen en un módulo. Sin embargo, un rasgo debe estar dentro del alcance para llamar a cualquier método en ese rasgo, incluso si un tipo que implementa ese rasgo ya está dentro del alcance. Por ejemplo, para usar el método read_to_string en un tipo que implementa el rasgo de lectura, debe usar std::io::Read.
La declaración de uso puede tener un comodín: use std::io::*. Se desaconseja esto porque no está claro qué artículos se importan y estos pueden cambiar con el tiempo.
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();
}
Podemos utilizar la palabra clave pub para hacer públicos los módulos. Además, existen especificadores pub(...) avanzados para restringir el alcance de la visibilidad pública.
Esto le indica a Rust que el contenido del módulo garden se encuentra en src/garden.rs. De manera similar, se puede definir un módulo garden::vegetables que va estar en src/garden/vegetables.rs.
Los módulos definidos en archivos también se pueden documentar mediante "comentarios internos del documento". Estos documentan el elemento que los contiene, por ejemplo:
//! This module implements the garden, including a highly performant germination
//! implementation.
// Re-export types from this module.
pub use garden::Garden;
pub use seeds::SeedPacket;
/// 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 un archivo 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
De manera similar, mod nos permite tipos de espacios de nombres y funciones:
mod foo {
pub fn do_something() {
println!("In the foo module");
}
}
mod bar {
pub fn do_something() {
println!("In the bar module");
}
}
fn main() {
foo::do_something();
bar::do_something();
}
Los paquetes brindan funcionalidad e incluyen un archivo Cargo.toml que describe cómo crear un paquete de más de 1 caja.
Las cajas son un árbol de módulos, donde una caja binaria crea un ejecutable y/o una caja de biblioteca se compila en una biblioteca.
Los módulos definen la organización, el alcance de los proyectos.
fn main() {
let primes = vec![2, 3, 5, 7];
let prime_squares = primes.into_iter().map(|p| p * p).collect::<Vec<_>>();
println!("prime_squares: {prime_squares:?}");
}
Iterator iteradores:
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>,
Self: Sized
Hay dos formas de especificar B para este método:
Hay implementaciones básicas de FromIterator para Vec, HashMap, etc. También hay implementaciones más especializadas que le permiten hacer cosas interesantes como convertir un Iterator<Item = Result<V, E>> en un Result<Vec<V>, E> .