Rust 101: Basics
Make sure Rust is installed-
rustc --version
Creating a new Rust project.
cargo new my_rust_project
main.rs
fn main() {
//first program
print!("Hello");
println!("Hello, world!");
}
Main function
In Rust, the main function is a special function that serves as the program's entry point. It is where the program's execution begins, and it must be present in every executable Rust program.
Running the code:
cargo run
Data Types:
Integers-
fn main() {
//Scalar types: int, float, boolean, char
//Unsigned - never negative - u8, u16, u32, u64, u128, usize
//Signed - can be negative and positive - i8, i16, i32, i64, i128, isize
println!("Max size of a u32: {}", u32::MAX);
println!("Max size of a u64: {}", u64::MAX);
println!("Max size of a i32: {}", i32::MAX);
println!("Max size of a i64: {}", i64::MAX);
}
Floats-
fn main() {
//floats - f32, f64 - 3.14
println!("Max size of a f32: {}", f32::MAX);
println!("Max size of a f64: {}", f64::MAX);
}
Boolean-
fn main() {
// Declare boolean variables
let is_raining = true;
let is_sunny = false;
// Use boolean variables in conditional statements
if is_raining {
println!("It's raining. Don't forget your umbrella!");
} else if is_sunny {
println!("It's sunny. A great day for a walk!");
} else {
println!("The weather is unclear. Stay prepared!");
}
// Boolean logic example
let is_outdoor_activity_possible = is_sunny && !is_raining;
println!("Is outdoor activity possible? {}", is_outdoor_activity_possible);
}
Char-
4 byte Character.
Variables-
immutable vs mutable
fn main() {
// Variables are immutable
let x: i32 = 5;
let y: i32 = 6;
println!("Addition in Rust: {} + {} = {}", x, y, x + y);
//Mutable variables can be created using mut keyword
let mut hello: &str ="Hello World!"; //mutable variable
println!("{}", hello);
hello = "Bye World!"; //We were able to change value of hello variable because we defined it as mutable
println!("{}", hello);
}
Constants-
// Constants
const NUMBER: i32 = 17;
fn main() {
const NUMBER2: i32 = 10;
println!("{}", NUMBER);
println!("{}", NUMBER2);
}
Suffixes and Underscores
fn main() {
//Suffixes are used to sepicify type of numeric variable
let a: u32 = 41_u32;
let b: i32 = -42i32;
println!("a = {}, b = {}", a, b);
//underscores are used for spacing just to make code more readable
let x = 1_000_000;
let y = 0.000_000_001;
println!("x = {}, y = {}", x, y);
}
Challenge 1-
/* Challenge 1 - Build a program that has the following:
1) Has a global constant integer named 'birthday' with a value of 1
2) Has a local string variable named 'my_name' with your name as the value
3) Has a local string variable named 'my_birthday' with your birth month/day (no year) as the value
4) Has a local mutable integer variable named 'age' with your current age as the value
5) Has a local integer variable named 'new_age' with your age after your birthday as the value
6) Prints out 'My name is X and I am X years old. I will turn X on X'
*/
Challenge 1 Solution-
const BIRTHDAY: i32 = 1;
fn main()
{
let my_name: &str ="n00ky";
let my_birthday: &str ="April";
let mut age = 24;
let new_age = age+BIRTHDAY ;
println!("My name is {} and I am {} years old. I will turn {} on {}.",my_name,age,new_age,my_birthday);
}
Tuples(Can store up to 12 values with different datatypes)-
fn main() {
// COMPOUND TYPES - Tuples
let student_a: (&str, char, f64) = ("n00ky", 'B', 3.58);
let student_b: (&str, char, f64) = ("n00kyy", 'C', 3.58);
let name_student_a: &str = student_a.0;
let grade_student_a: char = student_a.1;
let gpa_student_a: f64 = student_a.2;
let (name_student_b, grade_student_b, gpa_student_b) = student_b;
println!("My name is {}, my class grade is {}, my overall GPA is {}.",name_student_a, grade_student_a, gpa_student_a);
println!("My name is {}, my class grade is {}, my overall GPA is {}.",name_student_b, grade_student_b, gpa_student_b);
}
Arrays(Can store up to 32 values with same datatype)-
fn main() {
let names = ["Prabh","Bob","Tom"];
println!("The third student in our array is {}",names[2]);
}
Slicing in Arrays-
fn main() {
let ar =[1,2,3,4,5,6];
let slice= &ar[2..5];
println!("{:?}",slice);
}
Mutable arrays-
fn main() {
let ar =[1,2,3,4,5,6];
let slice= &ar[2..5];
println!("{:?}",slice);
let mut arr = ['A','B','C','D','E'];
let slicee = &mut arr[3..5];
slicee[0]= 'Z';
println!("{:?}",slicee);
}
Vector-
fn main() {
// Vector - Similar to an array
// Slower than arrays, but more flexible
let mut vec1: Vec<i32> = Vec::new();
let mut vec2: Vec<i32> = vec![1, 2, 3];
vec1.push(1);
vec2.push(4);
let second_element: i32 = vec2[1];
println!("The second element is {}", second_element);
println!("The length of the vector is {}", vec2.len());
for element in vec2.iter() {
println!("Element: {}", element);
}
}
Strings-
fn main() {
// STRINGS - There are several types
// Likely only use two: String and &str
// str - string slice, &str - borrowed string slice - cannot be modified
// String - data can be modified
// &str - Essentially a subset of a String
let mut name: String = String::new(); //First way of creating a string
let name1: String = String::from("Golu"); //Second way of creating a string
let name2 = "Bheem".to_string();//Third way of creating a string
name.push_str("Testing");
name.push_str(" test");
println!("{}", name);
println!("{}", name1);
println!("{}", name2);
}
Structures-
use std::io;
fn main() {
// Structures
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
let rect: Rectangle = Rectangle { width: 30, height: 50 };
let area: u32 = rect.area();
println!("The area of a rectangle is {}", area);
}
Enumeration or Enums-
It is a defined set of name-values.
fn main(){
// Enumeration or enum
// Define a set of named values
#[derive(Debug)]
enum Shape {
Circle(f32),
Rectangle(f32, f32),
}
let circle: Shape = Shape::Circle(10.0);
let rectangle: Shape = Shape::Rectangle(30.0, 40.0);
println!("{:?}", circle);
}
Generics-
fn main() {
// Generics- They are used to define functions with undefined return or input parameter types.
//For example the below function will work with int, float
fn sum<T: std::ops::Add<Output = T>>(a: T, b: T) -> T {
a + b
}
let x: i32 = sum(1, 2);
let y: f64 = sum(2.3, 3.5);
println!("The value of X is {} and y is {}", x,y);
println!("Sum of 2 + 2 = {}",sum(2,2));
struct Items<T>{
x:T,
y:T,
}
let i=Items{x:1.0,y:2.0};
println!("{},{}",i.x,i.y);
}
Input Output-
use std::io;
fn main() {
let mut name = String::new();
io::stdin().read_line( &mut name);
println!("Hello {}, Welcome!",name.trim_end());
}
Operators-
use std::io;
fn main() {
let mut x = String::new();
let mut y = String::new();
let _ =io::stdin().read_line(&mut x);
let _ =io::stdin().read_line(&mut y);
let xi32: i32 = x.trim().parse().unwrap();
let yi32: i32 = y.trim().parse().unwrap();
let xf64: f64 = x.trim().parse().unwrap();
let yf64: f64 = y.trim().parse().unwrap();
println!("+ is {}, - is {}, * is {}, / is {}, / in decimals is {}",xi32+yi32,xi32-yi32,xi32*yi32,xi32/yi32,xf64/yf64);
}
Dependencies-
use rand::Rng;
fn main() {
let x= rand::rng().random_range(1..100);
println!("{}",x);
}
Challenge 2-
/*
Build a simple calculator that takes two user inputs
then calculates the addition, subtraction, multiplication, and division
of those two inputs.
*/
Challenge 2 Solution-
use std::io;
fn main() {
let mut input_x = String::new();
io::stdin().read_line( &mut input_x);
let mut input_y = String::new();
io::stdin().read_line( &mut input_y);
let x: i32 = input_x.trim().parse().expect("Entry was not an integer!");
let y: i32 = input_y.trim().parse().expect("Entry was not an integer!");
println!("+ is {}, - is {}, * is {} and / is {}",x+y,x-y,x*y,x/y);
}
Comparison and Conditionals-
use std::io;
fn main() {
// if, else if, else
println!("How much money do you have?");
let mut input_money: String = String::new();
let _ = io::stdin().read_line(&mut input_money);
let money: i32 = input_money.trim().parse().expect("Entry was not an integer");
println!("How old are you?");
let mut input_age: String = String::new();
let _ = io::stdin().read_line(&mut input_age);
let age: i32 = input_age.trim().parse().expect("Entry was not an integer");
if (age >= 21) && (money >= 5) {
println!("We're getting a drink!");
} else if (age >= 21) && (money < 5) {
println!("Come back with more money!");
} else if (age < 21) && (money >= 5) {
println!("Nice try, kid!");
} else {
println!("You're too young and too poor.");
}
}
Match case-
use std::io;
fn main() {
let candidacy_age:i32= 32;
match candidacy_age{
1_i32..24_i32=>println!("Cannot hold office."),
25_i32..29_i32=>println!("Can run for the House."),
30_i32..34_i32=>println!("Can run for the senate."),
35_i32..=i32::MAX => println!("Can run for the president."),
_ => println!("Are you an infant?")
};
}
Conditionals using match-case-
use std::cmp::Ordering;
fn main() {
// Match - matching arm & all possible values must be covered
let my_age: i32 = 100;
let drinking_age: i32 = 21;
match my_age.cmp(&drinking_age) {
Ordering::Less => println!("Cannot drink!"),
Ordering::Equal => println!("Woo, you can drink!"),
Ordering::Greater => println!("Can drink!"),
};
}
Loops-
fn main() {
//LOOPS for, while, infinite
let veg: [&str; 3] = ["carrot", "Spinach", "potato"];
for x in veg.iter() {
println!("{}", x);
}
let mut x =10;
while x > 1{
println!("{}",x);
x = x -1;
}
loop{
x=x+1;
println!("Running forever");
}
}
Functions-
They are mini programs that contain reusable code.
fn main() {
println!("Hello, world!");
greet("Prabhsimran");
who_am_i();
add(5,5);
println!("{}",mul(5,5));
let (added,multiplied)=add_and_mul(5, 7);
println!("{} and {}",added,multiplied);
}
fn add_and_mul(x:i32,y:i32)->(i32,i32){
(x+y,x*y)
}
fn mul(x:i32,y:i32)->i32{
x*y
//return(x*y);// or we can speicfy return (x*y);
//return x*y; // or we can speicfy return x*y;
}
fn add(x:i32,y:i32){
println!("{}",x+y);
}
fn who_am_i(){
let name="Prabhsimran";
let age = 50;
println!("My name is {} and I am {} years old.",name,age);
}
fn greet(name: &str) {
println!("Hello, {}!", name);
}
Challenge 3-
fn main() {
// Create a calculator that takes three user inputs (x, y, and operator)
// Create functions for +, -, *, /
// Use if/else or Match for operator
// Might take a little research!
}
Solution-
use std::io;
fn main() {
let mut input_x: String = String::new();
io::stdin().read_line( &mut input_x);
let mut input_y: String = String::new();
io::stdin().read_line( &mut input_y);
let mut operator_str: String = String::new(); //operator +,-,*,/
io::stdin().read_line( &mut operator_str);
operator_str = operator_str.trim().to_string();
println!("running calculation for {}",operator_str);
let x: i32 = input_x.trim().parse().expect("Entry was not an integer!");
let y: i32 = input_y.trim().parse().expect("Entry was not an integer!");
let x_float: f32 = input_x.trim().parse().expect("Entry was not an float!");
let y_float: f32 = input_y.trim().parse().expect("Entry was not an float!");
if operator_str == "+"{
println!("+ = {}",add(x,y));
}
else if operator_str == "-" {
println!("- = {}",sub(x,y));
}
else if operator_str == "*" {
println!("* = {}",mul(x,y));
}
else if operator_str == "/" {
println!("* = {}",div(x_float,y_float));
}
}
fn add(x:i32,y:i32)->i32{
x+y
}
fn sub(x:i32,y:i32)->i32{
x-y
}
fn mul(x:i32,y:i32)->i32{
x*y
}
fn div(x:f32,y:f32)->f32{
x/y
}
Error Handling-
Manually using Ok and Err
fn main() {
// HELPER METHODS - unwrap(), expect()
// Two types of errors: recoverable and unrecoverable
// Recoverable - result enum & option enum
// Unrecoverable - panic! macro
// Result enum - Result<T, E>
// ? operator
let result: Result<i32, String> = divide(10, 0);
match result {
Ok(value) => println!("Result: {}", value),
Err(msg) => println!("Error: {}", msg),
}
println!("The show must go on!");
}
fn divide(x: i32, y: i32) -> Result<i32, String> {
if y == 0 {
return Err(String::from("Cannot divide by zero"));
}
Ok(x / y)
}
unwrap()
fn main() {
let result: i32 = dividee(10, 2).unwrap();
println!("Result: {}", result);
println!("The show must go on!");
}
fn dividee(x: i32, y: i32) -> Result<i32, String> {
if y == 0 {
return Err(String::from("Cannot divide by zero"));
}
Ok(x / y)
}
expect()
fn main() {
let result: i32 = dividee(10, 0).expect("Divide by zero error!");
println!("Result: {}", result);
println!("The show must go on!");
}
fn dividee(x: i32, y: i32) -> Result<i32, String> {
if y == 0 {
return Err(String::from("Cannot divide by zero"));
}
Ok(x / y)
}
panic
fn main() {
let result: i32 = divide(10,0);
println!("Result: {}", result);
println!("The show must go on!");
}
fn divide(x: i32, y: i32) -> i32 {
if y == 0 {
panic!("Cannot divide by zero.");
}
x / y
}
QuestionMark operator
use std::io;
use std::fs::File;
use std::io::Read;
fn read_file(path: &str) -> Result<String, std::io::Error> {
let mut file = match File::open(path) {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Ok(_) => Ok(contents),
Err(e) => Err(e),
}
}
fn main() {
let result = read_file("src/test.txt");
match result {
Ok(contents) => println!("File contents: {}", contents),
Err(err) => println!("Error reading file: {}", err),
}
}
Now, simplifying the above code using? operator-
use std::io;
use std::fs::File;
use std::io::Read;
fn read_file(path: &str) -> Result<String, std::io::Error> {
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
let result = read_file("src/test.txt");
match result {
Ok(contents) => println!("File contents: {}", contents),
Err(err) => println!("Error reading file: {}", err),
}
}
Pointers-
Raw Pointers-
fn main() {
let r: *const i32=&10;
unsafe{
println!("Value: {}",*r);
}
}
Define a raw pointer (r):
- let r: *const i32 = &10;
- This creates a raw pointer (r) pointing to the integer 10.
- Note that *const i32 is a constant raw pointer, meaning it cannot be modified after being initialized.
Use an unsafe block:
- Raw pointers in Rust require explicit unsafe code for dereferencing.
- unsafe { println!(βValue: {}β, *r); }
- Here, *r is dereferenced, meaning we access the value that r points to.