First of all, I dabbled in Rust a couple of years ago, but that was it—just going over data types and syntax. But I want to learn more about this beast that blows every benchmark out of the water.
Refreshing our knowledge and learning more about the basics
First, I started looking for a worthwhile Spanish course on YouTube, and that's when I came across this 4-hour course in Spanish, which is really great.

After an hour of reviewing the material, I created my first Cargo project for 2024.
fn main() {
println!("Hello, chupapis!");
}Yeah, that's what I was expecting xD.

Creating Something More Elaborate
Anyway, I couldn't just leave it at that. I wanted to do something simple to start with. What could be better than a CLI that creates a user and then displays the user's information?
fn main() {
println!("Hello, chupapis!");
}
struct User {
name: String,
age: i8,
email: String,
birthday: // No hay Date .___.
}Hmm... Rust doesn't have a `Date` type 🥲
Faced with these problems: “Time to ask ChatGPT 👍.”

That's so cool! 😯 And he gave me this example:
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,
}What I didn't know was that….

from_ymd This is deprecated 🥲
But problems are meant to be solved. I'll use the recommended one. from_ymd_opt .
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,
}Now that was a whole different story. 😎
But it still lacked interactivity, so now I had to learn how to receive input.
use std::io::stdin as input;Yep, just to make it more fun.

And after a few minutes, I ended up with this. 💀
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,
}Yeah, a lot of “talking to myself,” but it works at the end of the day.
Let's refactor it.
The first thing I did was make a read_line generic. the variable's responsibility for retrieving the content “RAW.” It would remain within the function itself, so that the scope would eliminate the variable when the function ends. And of course, the only thing it would return would be a string clean and with line breaks \n. 👍
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()
}Now, in the case of the integer First, I did something like this:
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")
};
}
}But I feel like something's off xD
I'd have to learn "generics" so I can use any type of data I want, just like we do with TypeScript generics.
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 stringI figured this was already pretty advanced, so I went to ask ChatGPT.
He gave me this from here.
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."),
}
}
}The most exotic thing would be that "guy" from the opening credits, std::str::FromStr and what he told me was:

The only thing I can say is that I already know why Rust is so secure—it even restricts you when it comes to generics, requiring you to specify the exact operations that need to be PERFORMED so that the… function works correctly—pardon the repetition xD
And what I ended up with was this function.
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")
};
}
}By not catching the error returned by the parse if it fails in the match, I just take away the where That's syntax I'm not familiar with yet, but I suppose it was meant to ensure that the error can be printed. This makes it a little cleaner.
And finally, create a function to read a date.
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")
}And the read_int It already returns the types required by the method from_ymd_opt automatically.
I'm refactoring the main and it would look like this:
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
}and when I run it, I get this result:

And that's all for now. I guess the next step is to build a REST API. I'll start looking into whether to choose Actix, Axum, or Rocket.