Antes que nada, ya toque rust hace un par de años pero nada mas despues de eso, tocando tipos de datos y sintaxis. Pero quiero aprender mas de este camion que detona cualquier brenchmark.

Refrescando y aprendiendo mas de las bases

Primero me puse a buscar algun curso en español en yt que valiera la pena y en eso me encontre este de 4 horas en español que es un gusto bueno.

Imagen del artículo


Despues de una hora de repaso cree mi primer projecto Cargo del 2024.

rust
fn main() {
    println!("Hello, chupapis!");
}

Si, que esperaba xD.

Imagen del artículo

Creando algo mas elaborado

Anyways, no podía quedarme solo con eso. Quería hacer algo simple para empezar. que mejor que un CLI que cree un usuario y que al final te diga su información.

rust
fn main() {
    println!("Hello, chupapis!");
}

struct User {
    name: String,
    age: i8,
    email: String,
    birthday: // No hay Date .___.
}

Mmhhhh….. Rust no tiene algún tipo Date 🥲

Ante estos problemas: “Toca preguntar a ChatGPT 👍”.

Imagen del artículo

Que pro! 😯 y me dio este ejemplo:

rust
use chrono::NaiveDate;

fn main() {
    let user = User {
        name: String::from("Diego"),
        age: 22,
        email: String::from("diego@example.com"),
        birthday: NaiveDate::from_ymd(2002, 3, 28), // Año, mes, día
    };

    println!(
        "Nombre: {}, Edad: {}, Email: {}, Cumpleaños: {}",
        user.name, user.age, user.email, user.birthday
    );
}

struct User {
    name: String,
    age: i8,
    email: String,
    birthday: NaiveDate,
}

Lo que no sabia es que….

Imagen del artículo

from_ymd esta deprecado 🥲

Pero los problemas están para solucionarse. Usare el recomendado from_ymd_opt .

rust
fn main() {
    let user = User {
        name: String::from("Diego Musagy"),
        age: 22,
        email: String::from("diego.1203.dm@gmail.com"),
        birthday: NaiveDate::from_ymd_opt(2002, 3, 28)
            .expect("Fecha inválida"),
    };

    println!(
        "Nombre: {}, Edad: {}, Email: {}, Cumpleaños: {}",
        user.name, user.age, user.email, user.birthday
    );
}

struct User {
    name: String,
    age: i8,
    email: String,
    birthday: NaiveDate,
}

Ahora si esto ya era otra cosita pues. 😎

Pero aun le faltaba la interactividad así que ahora tenia que aprender a recibir el input.

rust
use std::io::stdin as input;

Sip, para que sea mas ameno.

Imagen del artículo

Y después de unos minutos tuve esto. 💀

rust
use chrono::NaiveDate;
use std::io::stdin as input;

fn main() {
    let mut name = String::from("");
    let mut age_str = String::from("");
    let age: u8;
    let mut email = String::from("");
    let mut bd_year_str = String::from("");
    let bd_year: i32;
    let mut bd_month_str = String::from("");
    let bd_month: u32;
    let mut bd_day_str = String::from("");
    let bd_day: u32;
    
    println!("¿Cual es tu nombre?");
    input().read_line(&mut name)
        .expect("error al leer la entrada");

    println!("¿Cual es tu edad?");
    input().read_line(&mut age_str)
        .expect("error al leer la entrada");
    age = age_str.trim()
        .parse()
        .expect("Por favor. Inserte un numero correcto.");

    println!("¿Cual es tu email?");
    input().read_line(&mut email)
        .expect("error al leer la entrada");
    
    println!("¿Cual es tu cumpleaños?");
    println!("\tAño: ");
    input().read_line(&mut bd_year_str)
        .expect("error al leer la entrada");
    println!("\tMes: ");
    input().read_line(&mut bd_month_str)
        .expect("error al leer la entrada");
    println!("\tDia: ");
    input().read_line(&mut bd_day_str)
        .expect("error al leer la entrada");
    
    bd_year = bd_year_str.trim()
        .parse()
        .expect("Por favor. Inserte un numero correcto.");
    bd_month = bd_month_str.trim()
        .parse()
        .expect("Por favor. Inserte un numero correcto.");
    bd_day = bd_day_str.trim()
        .parse()
        .expect("Por favor. Inserte un numero correcto.");

    let birthday = NaiveDate::from_ymd_opt(bd_year, bd_month, bd_day)
        .expect("Fecha invalida");

    let user = User {
        name,
        age,
        email,
        birthday,
    };

    println!(
        "Nombre: {}, Edad: {}, Email: {}, Cumpleaños: {}",
        user.name, user.age, user.email, user.birthday
    );
}

struct User {
    name: String,
    age: u8,
    email: String,
    birthday: NaiveDate,
}

Si, mucho “repetirme a mi mismo” pero funcional al final del día.

Vamos a refactorizarlo.

Primer lo que hice fue un read_line genérico. la responsabilidad de la variable para obtener el contenido “RAW”. Quedaría en la propia función para que el propio scope elimine la variable al finalizar la función. Y claro lo único que retornaría seria un string limpio y si salto de linea \n. 👍

rust
fn read_line (placeholder: &str) -> String {
    println!("{}", placeholder);
    let mut input_var= String::from("");
    input()
        .read_line(&mut input_var)
        .expect("error al leer la entrada");
    input_var.trim().to_string()
}

Ahora en el caso de los integer primero hice algo así:

rust
fn read_u8(placeholder: &str) -> u8 {
    loop {
        let raw = read_line(placeholder);
        match raw.parse() {
            Ok(value) => return value,
            Err(_) => println!("Inserte un numero valido")
        };
    }   
}
fn read_i32(placeholder: &str) -> i32 {
    loop {
        let raw = read_line(placeholder);
        match raw.parse() {
            Ok(value) => return value,
            Err(_) => println!("Inserte un numero valido")
        };
    }   
}
fn read_u32(placeholder: &str) -> u32 {
    loop {
        let raw = read_line(placeholder);
        match raw.parse() {
            Ok(value) => return value,
            Err(_) => println!("Inserte un numero valido")
        };
    }   
}

pero siento que algo esta mal xD

Me tocaría aprender “genéricos” para poder poner cualquier tipo de dato que quiera como lo hacemos con los genéricos de Typescript.

typescript
function firstElement<T>(array: T[]): T | undefined {
    return array[0];
}

let primerNumero = firstElement([1, 2, 3]);          // T es number
let primeraPalabra = firstElement(["a", "b", "c"]);  // T es string

Esto ya era algo avanzado suponía, así que me fui a preguntarle a real Chatgpt.

Me dio esto de acá.

rust
fn read_int<T: std::str::FromStr>(placeholder: &str) -> T
where
    T::Err: std::fmt::Debug,
{
    loop {
        let raw = read_line(placeholder);
        match raw.parse::<T>() {
            Ok(value) => return value,
            Err(_) => println!("Por favor, ingrese un número válido."),
        }
    }
}

lo mas exótico seria ese “tipo” del genérico, std::str::FromStr y lo que me dijo fue:

Imagen del artículo

Lo único que puedo decir que ya se porque Rust es muy seguro, limitándote inclusive con genéricos, indicándole las operaciones necesarias a HACER para que funcione correctamente la…. función valga la redundancia xD

y lo que saque al final fue esta función.

rust
fn read_int<T: std::str::FromStr>(placeholder: &str) -> T {
    loop {
        let raw = read_line(placeholder);
        match raw.parse::<T>() {
            Ok(value) => return value,
            Err(_) => println!("Inserte un numero valido")
        };
    }   
}

Al no usar el error que devuelve el parse si falla en el match, simplemente le quito el where que es sintaxis que aun no conozco pero supongo que habrá sido para validad que el error se pueda imprimir. Así quedando algo mas limpio.

Y ya por ultimo hacer una función para leer una fecha.

rust
fn read_date(placeholder: &str) -> NaiveDate {
    println!("{}", placeholder);
    let year = read_int("Año: ");
    let month = read_int("Mes: ");
    let day = read_int("Dia: ");
    
    NaiveDate::from_ymd_opt(year, month, day)
        .expect("Fecha invalida")
}

Y el read_int ya devuelve los tipos necesarios que necesita el método from_ymd_opt automáticamente.

Refactorizo el main y quedaría así:

rust
use chrono::NaiveDate;
use std::io::stdin as input;

fn main() {
    let name = read_line("¿Cual es tu nombre?");
    let age = read_int("¿Cual es tu edad?");
    let email = read_line("¿Cual es tu Email?");
    let birthday = read_date("¿Cual es tu cumpleaños?");

    let user = User {
        name,
        age,
        email,
        birthday
    };
    
    println!("Hello, chupapis!");

    println!(
        "Nombre: {}, Edad: {}, Email: {}, Cumpleaños: {}",
        user.name, user.age, user.email, user.birthday
    );
}

fn read_line (placeholder: &str) -> String {
    println!("{}", placeholder);
    let mut input_var= String::from("");
    input()
        .read_line(&mut input_var)
        .expect("error al leer la entrada");
    input_var.trim().to_string()
}

fn read_int<T: std::str::FromStr>(placeholder: &str) -> T {
    loop {
        let raw = read_line(placeholder);
        match raw.parse::<T>() {
            Ok(value) => return value,
            Err(_) => println!("Inserte un numero valido")
        };
    }   
}

fn read_date(placeholder: &str) -> NaiveDate {
    println!("{}", placeholder);
    let year = read_int("Año: ");
    let month = read_int("Mes: ");
    let day = read_int("Dia: ");
    
    NaiveDate::from_ymd_opt(year, month, day)
        .expect("Fecha invalida")
}

struct User {
    name: String,
    age: u8,
    email: String,
    birthday: NaiveDate
}

y al ejecutarlo logro tener este resultado:

Imagen del artículo

Y eso seria todo por ahora. Supongo que el siguiente paso es hacer una api REST. Y me pondré a ver cual escoger entre Actix, Axum o Rocket.