Skip to content

Rust Programming

Posted on:March 27, 2023 at 07:23 PM

Installation & Tool

brew install rustup
rustup-init

Fundamentals

Data Types

Basic Types

Variables

What’s a variable?

let two = 2;
let hello = "Hello";
let j = 'j';
let pi = 3.14;
let mut my_name = "Bill";
let quit_program = false;

Functions

What’s a function?

fn add(a: i32, b: i32) -> i32 {
    a + b
}

The println macro

let life = 42;
println!("The answer to life is {:?}", life);
println!("{:?} {:?}", life, life);

Control Flow

if age < 18 {
    println!("You are under 18");
} else if age >= 18 && age <= 22 {
    println!("You are on college")
} else {
    println!("You are good to work");
}

Match

let value = 3;
match value {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("other"),
}

Loop

let mut i = 0;

// loop
loop {
    if (i == 10) {
        break:
    }
    println!("{:?}", i);
    i += 1;
}

// while
while i != 10 {
    println!("{:?}", i);
    i += 1;
}

Enums

enum Direction {
    Up,
    Down,
    Left,
    Right,
}

fn which_direction(go: Direction) -> &'static str {
    match go {
        Direction::Up => "up",
        Direction::Down => "down",
        Direction::Left => "left",
        Direction::Right => "right",
    }
}

Structs

struct ShippingBox {
    width: i32,
    height: i32,
    depth: i32,
}

}
fn main() {
    let my_box = ShippingBox {
        width: 10,
        height: 20,
        depth: 30,
    };
    println!("The box is {:?} tall", my_box.height);
}

Tuples

fn one_two_three() -> (i32, i32, i32) {
    (1, 2, 3)
}
let numbers = one_two_three();
println!("one: {:?}, two: {:?}, three: {:?}", numbers.0, numbers.1, numbers.2);

let (one, two, three) = numbers;
println!("one: {:?}, two: {:?}, three: {:?}", one, two, three);

enum Access {
    Full,
}
let (employee, access) = ("John", Access::Full);

Expressions

let my_num = 3;
let is_lt_5 = if my_num < 5 { true } else { false };
let is_gt_5 = my_num > 5;
let is_three = match my_num {
    3 => true,
    _ => false,
};
enum Access {
    Admin,
    User,
    Guest,
}
let access_level = Access::Guest;
let system_is_hacked = true;
let can_access = match access_level {
    Access::Admin => true,
    _ => {
        if system_is_hacked == true {
            true
        } else {
            false
        }
    },
};

Intermediate Memory

Memory

Addressed

Offsets

Address and Offset

Ownership

enum Light {
    Red,
    Yellow,
    Green,
}
fn display_light(light: &Light) {
    match light {
        Light::Red => println!("Red"),
        Light::Yellow => println!("Yellow"),
        Light::Green => println!("Green"),
    }
}
fn main() {
    let red = Light::Red;
    // instead of moving the ownership into this function, we're going to have this function just borrow the data
    display_light(&red);
    display_light(&red);
}

impl

enum Color {
    Red,
    Green,
    Blue,
}
impl Color {
    fn display(&self) {
        match self {
            Color::Red => println!("Red"),
            Color::Green => println!("Green"),
            Color::Blue => println!("Blue"),
        }
    }
}

struct Dimension {
    length: i32,
    width: i32,
    height: i32,
}
impl Dimension {
    fn display(&self) {
        println!("Length: {:?}", self.length);
        println!("Width: {:?}", self.width);
        println!("Height: {:?}", self.height);
    }
}

struct ShippingBox {
    weight: i32,
    dimension: Dimension,
    color: Color,
}
impl ShippingBox {
    fn new(weight: i32, dimension: Dimension, color: Color) -> Self {
        Self {
            weight,
            dimension,
            color
        }
    }
    fn display(&self) {
        println!("Weight: {:?}", self.weight);
        self.dimension.display();
        self.color.display()
    }
}



fn main() {
    let small_box = Dimension {
        length: 1,
        width: 2,
        height: 3,
    };

    let shipping_box = ShippingBox::new(1, small_box, Color::Green);
    shipping_box.display();
}

Vectors

let mut my_numbers = Vec::new();
my_numbers.push(1);
my_numbers.push(2);   vb
my_numbers.push(3);
my_numbers.pop();
println!("length is {:?}", my_numbers.len());
println!("{:?}", my_numbers);
println!("{:?}", my_numbers[0]);

let numbers = vec![1,2,3,4,5];
for number in numbers {
    println!("for in loop: {}", number);
}

Strings

example 1:

fn display(data: &str) {
    println!("Hello, {:?}", data);
}

fn main() {
    // by default borrowed
    let name = "kevin";
    display(name);

    let owned_string = "owned".to_owned();
    let another_owned_string = String::from("another");
    display(&owned_string);
    display(&another_owned_string);
}

example 2:

struct LineItem {
    name: String,
    count: i32,
}

fn display_name(data: &str) {
    println!("name: {:?}", data);
}

fn main() {
    let receipts = vec![LineItem {
        name: "apple".to_owned(),
        count: 1,
    }, LineItem {
        name: String::from("banana"),
        count: 2,
    }, LineItem {
        name: "cherry".to_owned(),
        count: 3,
    }];

    for item in receipts {
        display_name(&item.name)
    }
}

Derive

For example, if we want to print out the data of enums or whole struct, we can derive the debug printing functionality.

// All fields within the struct enums, which is Position here, also have to derive that functionality, which is Debug
#[derive(Debug)]
enum Position {
    Manager,
    Developer,
    Tester,
}

#[derive(Debug)]
struct Employee {
    name: String,
    position: Position,
}

fn main() {
    let me = Employee {
        name: String::from("Kevin"),
        position: Position::Developer,
    };

    // match me.position {
    //     Position::Manager => println!("manager"),
    //     Position::Developer => println!("developer"),
    //     Position::Tester => println!("tester"),
    // }

    println!("My position is {:?}", me.position);
    println!("Employee data: {:?}", me)
}

3 extremely common derives:

#[derive(Debug, Clone, Copy)]
enum Position {
    Manager,
    Developer,
    Tester,
}

#[derive(Debug, Clone, Copy)]
struct Employee {
    work_hours: i32,
    position: Position,
}

// this function takes ownership and not borrowing
fn print_employee(employee: Employee) {
    println!("Employee data: {:?}", employee)
}

fn main() {
    // 'me' variable is copied and passed to the called function since Employee derives Clone and Copy
    let me = Employee {
        work_hours: 40,
        position: Position::Developer,
    };
    print_employee(me); // a new copy is made and pass to the function
    print_employee(me); // a second new copy is made and pass to the function
}

Type Annotations

// Basic: variables, functions
enum Fruit {
    Apple,
    Banana,
    Orange,
}
fn display_name(value: &str) {
    println!("Name: {:?}", value);
}
fn main() {
    let name: String = "John".to_owned();
    let age: i32 = 36;
    let letter: char = 'a';
    let apple: Fruit = Fruit::Apple;
    display_name(&name)
}

// Generic
let numbers: Vec<i32> = vec![1, 2, 3, 4, 5];
let fruits: Vec<Fruit> = vec![Fruit::Apple, Fruit::Banana, Fruit::Orange];

Advanced Match

enum PromotionDiscount {
    NewUser,
    Holiday(String),
}
enum Discount {
    Percentage(f32),
    Flat(f32),
    Promotion(PromotionDiscount),
    Custom(String),
}

Allow you to match on data associated with enums & structs.

enum Discount {
    Percentage(f32),
    Flat(i32),
}
enum Ticket {
    Backstage(String, i32),
    Vip(String, i32),
    Standard(i32),
}

fn main() {
    let flat = Discount::Flat(10);
    match flat {
        Discount::Flat(2) => println!("Flat 2.0"),
        Discount::Flat(value) => println!("Flat {}", value),
        _ => ()
    }

    let tickets = vec![
        Ticket::Backstage(String::from("John"), 50),
        Ticket::Vip(String::from("Amy"), 200),
        Ticket::Standard(100),
    ];
    for ticket in tickets {
        match ticket {
            Ticket::Backstage(name, price) => println!("Backstage ticket Holder: {:?}, Price: {:?}", name, price),
            Ticket::Vip(name, price) => println!("Vip ticket Holder: {:?}, Price: {:?}", name, price),
            Ticket::Standard(price) => println!("Standard ticket Price: {:?}", price),
        }
    }
}

Option type

Definition

enum Option<T> {
    Some(T),
    None,
}

Example:

struct Customer {
    age: Option<i32>, // optional field
    email: String,
}

// optional return type
fn find_customer_by_email(customers: Vec<Customer>, email: &str) -> Option<Customer> {
    for customer in customers {
        if customer.email == email {
            return Some(customer);
        }
    }
    None
}

fn main() {
    let customers = vec![
        Customer {
            age: Some(30),
            email: String::from("mark@example.com"),
        },
        Customer {
            age: None,
            email: String::from("becky@example.com"),
        },
    ];
    let found = find_customer_by_email(customers, "mark@example.com");

    match found {
        Some(customer) => match customer.age {
            Some(age) => println!("Customer age: {}", age),
            None => println!("Customer age not found"),
        },
        None => println!("Customer not found"),
    }
}

Documentation

/// Adds two numbers
fn add(a: i32, b: i32) -> i32 {
    a + b
}

Result type

Definition

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Example:

struct SoundData {
    data: String,
}

impl SoundData {
    fn new(input: &str) -> Self {
        Self {
            data: input.to_owned(),
        }
    }
}

fn get_sound(input: &str) -> Result<SoundData, String> {
    if input == "alert" {
        Ok(SoundData::new("alert"))
    }
    else {
        Err("Sound not found".to_owned())
    }
}

fn main() {
    let sound = get_sound("alert");
    match sound {
        Ok(sound) => println!("Sound: {}", sound.data),
        Err(err) => println!("Error: {}", err),
    }
}