Chapter 1

This book was created with mdBook

It's very simple to create a book with rust, with interactive code snippets.

Everything here is created in the folder /theBook

Building the book

Just run the following command:

./build.sh

To Work on it Interactive

mdbook watch -o

Here's what's happening:

mdbook build
rm -rf ../docs
mv book ../docs

And, that's it! Here is the first example of interactive code.

fn main() {
    // Some code
    println!("Hello, world!");
}

A more advanced example

macro_rules! test {
    // Arguments don't need to be separated by a comma.
    // Any template can be used!
    ($left:expr; and $right:expr) => {
        println!("{:?} and {:?} is {:?}",
                 stringify!($left),
                 stringify!($right),
                 $left && $right)
    };
    // ^ each arm must end with a semicolon.
    ($left:expr; or $right:expr) => {
        println!("{:?} or {:?} is {:?}",
                 stringify!($left),
                 stringify!($right),
                 $left || $right)
    };
}

fn main() {
    test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
    test!(true; or false);
}

Useful Basic Guide

Sample with tests.

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

fn main() {
    println!("{}", add_integer(1, 2));
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_add_integer() {
        assert_eq!(super::add_integer(1, 2), 3);
    }
}

Sample with fmt skips

fn vec_vec() -> Vec<Vec<i32>> {
    #[rustfmt::skip]
    let  vec = vec![
        vec![1, 2, 3],
        vec![4, 5, 6],
        vec![7, 8, 9],
    ];
    vec
}

fn main() {
    let vec = vec_vec();
    println!("{:?}", vec);
}

Lifetimes

This gets a little more complicated. But, it's important.

We have to make sure all the variables have the same lifetime.

fn greet<'a>(
    names: &mut Vec<&'a str>,
    target_name: &'a str,
    custom_message: &'a str,
    standard_message: &'a str,
) {
    for name in names.iter_mut() {
        *name = match name {
            &mut n if n == target_name => custom_message,
            _ => standard_message,
        }
    }
}

fn main() {
    let greeting = "Well, hello Ferris!";
    let mut names = vec!["Bob", "Frank", "Ferris"];
    greet(&mut names, "Ferris", greeting, "hi");
    println!("{:?}", names);
}

// Ref:
// https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/lifetimes.html

Working with Files

use std::fs::File;
use std::io::prelude::*;

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 modify_names(names: &mut Vec<&str>, custom_message: &str) {
    names.iter_mut().for_each(|i| {
        let new_name = match *i {
            "Bob" => format!("{}!, What's happening?", *i),
            _ => format!("Hi {} {}", *i, custom_message),
        };
        *i = Box::leak(new_name.into_boxed_str());
    });
}

fn main() {
    match read_file("./data/file.txt") {
        Ok(contents) => {
            let mut lines = contents.lines().collect();
            modify_names(&mut lines, ", custom message!");
            println!("{:?}", lines);
        }
        Err(e) => panic!("Error reading file: {:?}", e),
    }
}

Working with Errors

use std::io::{Error, ErrorKind};

fn find(substring: &str) -> Result<String, Error> {
    let strings: Vec<String> = vec![
        "apple".to_string(),
        "banana".to_string(),
        "cherry".to_string(),
    ];

    if let Some(s) = strings.iter().find(|s| s.contains(substring)) {
        println!("Found '{}' in '{}'", substring, s);
        Ok(s.to_string())
    } else {
        Err(Error::new(
            ErrorKind::NotFound,
            format!("No match for '{}'", substring),
        ))
    }
}

fn main() {
    let result = find("an");

    if result.is_err() {
        println!("Error: {:?}", result.err());
    } else {
        println!("Result: {:?}", result.unwrap());
    }
}

Favorite

fn main() {
    let strings = vec![
        String::from("hello"),
        String::from("world"),
        String::from("bobcat"),
        String::from("bob"),
        String::from("alabama"),
    ];

    let mut filtered_strings: Vec<String> = strings
        .iter()
        .filter(|s| s.contains("bob"))
        .inspect(|s| println!("Filtered string: {:?}", s))
        .map(|s| s.clone())
        .collect();

    filtered_strings.sort();
    println!("Filtered strings: {:?}", filtered_strings);
}

Here's another one

fn main() {
    let strings = vec![
        String::from("hello"),
        String::from("world"),
        String::from("bobcat"),
        String::from("bob"),
        String::from("alabama"),
    ];

    let mut filtered_strings: Vec<String> = strings
        .iter()
        .filter_map(|s| {
            if s.contains("bob") {
                Some(s.clone())
            } else {
                None
            }
        })
        .collect();

    filtered_strings.sort();
    println!("Filtered strings: {:?}", filtered_strings);
}

Useful

This can also be useful

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    if let Some(third_number) = numbers.iter().nth(2) {
        println!("The third number is {}", third_number);
    } else {
        println!("There is no third number.");
    }
}

There's more

fn main() {
    let strings = vec![
        String::from("hello"),
        String::from("world"),
        String::from("bobcat"),
        String::from("bob"),
        String::from("alabama"),
    ];

    let mut filtered_strings: Vec<String> = strings
        .iter()
        .filter(|s| s.contains("bob"))
        .inspect(|s| println!("Filtered string: {:?}", s))
        .filter(|s| sporky(s))
        .map(|s| s.clone())
        .collect();

    filtered_strings.sort();
    println!("Filtered strings: {:?}", filtered_strings);
}

fn sporky(string: &str) -> bool {
    if string.contains("cat") {
        println!("{} is sporky", string);
        true
    } else {
        println!("{} is not sporky", string);
        false
    }
}

You gotta love maps

fn main() {
    let strings = vec![
        String::from("1 2 3 4"),
        String::from("hello world"),
        String::from("goodbye world"),
        String::from("hello bobcat"),
        String::from("goodbye bobcat"),
        String::from("hello alabama"),
        String::from("goodbye alabama"),
    ];

    let filtered_strings: Vec<String> = strings
        .iter()
        .inspect(|s| println!("Before string: {:?}", s))
        .flat_map(|s| s.split_whitespace())
        .inspect(|s| println!("After string: {:?}", s))
        .filter(|s| s.contains("bob"))
        .map(|s| s.to_owned())
        .collect();

    println!("Filtered strings: {:?}", filtered_strings);
}

Chapter 2

This is an example of chapter 2.

enum Message {
    Quit,
    ChangeColor(u8, u8, u8),
    Move { x: i32, y: i32 },
    Write(String),
}

fn process_message(msg: Message) {
    // Using match expression
    match msg {
        Message::Quit => println!("Quit"),
        Message::ChangeColor(r, g, b) => println!("Change color to ({}, {}, {})", r, g, b),
        Message::Move { x, y } => println!("Move to coordinates ({}, {})", x, y),
        Message::Write(text) => println!("Write: {}", text),
    }
}

fn main() {
    let msg1 = Message::ChangeColor(255, 0, 0);
    let msg2 = Message::Write(String::from("Hello, World!"));
    let _msg3 = Message::Quit;
    let _msg4 = Message::Move { x: 10, y: 20 };

    // Using if let statement
    if let Message::ChangeColor(r, g, b) = msg1 {
        println!("Change color to ({}, {}, {})", r, g, b);
    } else {
        println!("Not a ChangeColor message");
    }

    process_message(msg1);
    process_message(msg2);
}

Chapter 3

SOLID Principles

Dependency Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions.

// Define an abstraction (trait) for data storage
pub trait DataStorage {
    fn save_data(&self, data: &str) -> Result<(), String>;
}

// Implement a low-level module for file storage
pub struct FileStorage;

impl DataStorage for FileStorage {
    fn save_data(&self, data: &str) -> Result<(), String> {
        println!("Saving data to a file: {}", data);
        // Implement file saving logic here
        Ok(())
    }
}

// Implement another low-level module for cloud storage
pub struct CloudStorage;

impl DataStorage for CloudStorage {
    fn save_data(&self, data: &str) -> Result<(), String> {
        println!("Saving data to the cloud: {}", data);
        // Implement cloud saving logic here
        Ok(())
    }
}

// High-level module that depends on the abstraction (trait) rather than concrete implementations
pub struct DataManager<T: DataStorage> {
    storage: T,
}

impl<T: DataStorage> DataManager<T> {
    pub fn new(storage: T) -> Self {
        DataManager { storage }
    }

    pub fn save_data(&self, data: &str) -> Result<(), String> {
        self.storage.save_data(data)
    }
}

fn main() {
    // Use FileStorage with DataManager
    let file_storage = FileStorage;
    let data_manager_file = DataManager::new(file_storage);
    data_manager_file.save_data("Some data for file storage").unwrap();

    // Use CloudStorage with DataManager
    let cloud_storage = CloudStorage;
    let data_manager_cloud = DataManager::new(cloud_storage);
    data_manager_cloud.save_data("Some data for cloud storage").unwrap();
}

DIP Another example

pub trait Bird {
    fn bird_call(&self);
}

pub struct Chicken {
    pub sound: String,
}

impl Bird for Chicken {
    fn bird_call(&self) {
        println!("{}", self.sound);
    }
}

pub struct Duck {
    pub sound: String,
}

impl Bird for Duck {
    fn bird_call(&self) {
        println!("{}", self.sound);
    }
}

pub struct BirdCage<T: Bird> {
    pub bird: T,
}

impl<T: Bird> BirdCage<T> {
    pub fn new(bird: T) -> Self {
        Self { bird }
    }

    pub fn make_sound(&self) {
        self.bird.bird_call();
    }
}

fn main() {
    let chicken = Chicken {
        sound: String::from("Cluck!"),
    };
    let duck = Duck {
        sound: String::from("Quack!"),
    };

    let chicken_cage = BirdCage::new(chicken);
    let duck_cage = BirdCage::new(duck);

    chicken_cage.make_sound();
    duck_cage.make_sound();
}

DIP bank example

pub trait Transaction {
    fn execute(&self, balance: &mut f64);
}

pub struct Deposit {
    pub amount: f64,
}

impl Transaction for Deposit {
    fn execute(&self, balance: &mut f64) {
        *balance += self.amount;
        println!("Deposit: ${}", self.amount);
    }
}

pub struct Withdraw {
    pub amount: f64,
}

impl Transaction for Withdraw {
    fn execute(&self, balance: &mut f64) {
        if *balance >= self.amount {
            *balance -= self.amount;
            println!("Withdraw: ${}", self.amount);
        } else {
            println!("Withdrawal failed: Insufficient funds");
        }
    }
}

pub struct Bank {
    pub transactions: Vec<Box<dyn Transaction>>,
    pub balance: f64,
}

impl Bank {
    pub fn new() -> Self {
        Self {
            transactions: Vec::new(),
            balance: 0.0,
        }
    }

    pub fn add_transaction<T: Transaction + 'static>(&mut self, transaction: T) {
        self.transactions.push(Box::new(transaction));
    }

    pub fn process_transactions(&mut self) {
        for transaction in &self.transactions {
            transaction.execute(&mut self.balance);
        }
    }
}

fn main() {
    let deposit = Deposit { amount: 1000.0 };
    let withdraw = Withdraw { amount: 500.0 };
    let invalid_withdraw = Withdraw { amount: 2000.0 };

    let mut bank = Bank::new();
    bank.add_transaction(deposit);
    bank.add_transaction(withdraw);
    bank.add_transaction(invalid_withdraw);

    bank.process_transactions();
}
In this example, we have a Transaction trait that represents the abstraction, and two structs, Deposit and Withdraw, that implement the Transaction trait. The Bank struct is a high-level module that depends on the Transaction trait, an abstraction, rather than depending on the concrete implementations (Deposit and Withdraw).
The Bank struct maintains a vector of Box to store transactions. This allows it to work with any kind of transaction without having to know the specifics of each type of transaction. The main function demonstrates the usage of the Bank struct with both a Deposit and a Withdraw instance.
The above example follows the Dependency Inversion Principle, as both high-level and low-level modules depend on an abstraction, and the abstraction does not depend on any implementation details.

Interface Segregation Principle

Many client-specific interfaces are better than one general-purpose interface.

// Define smaller, more specific traits
trait Swimmer {
    fn swim(&self);
}

trait Flyer {
    fn fly(&self);
}

trait Walker {
    fn walk(&self);
}

// Define the structs that will implement the traits
struct Penguin;
struct Duck;
struct Eagle;

// Implement the traits for each struct
impl Swimmer for Penguin {
    fn swim(&self) {
        println!("Penguin swims.");
    }
}

impl Swimmer for Duck {
    fn swim(&self) {
        println!("Duck swims.");
    }
}

impl Flyer for Duck {
    fn fly(&self) {
        println!("Duck flies.");
    }
}

impl Flyer for Eagle {
    fn fly(&self) {
        println!("Eagle flies.");
    }
}

impl Walker for Eagle {
    fn walk(&self) {
        println!("Eagle walks.");
    }
}

// Use the specific traits in functions that require them
fn swim(swimmer: &impl Swimmer) {
    swimmer.swim();
}

fn fly(flyer: &impl Flyer) {
    flyer.fly();
}

fn walk(walker: &impl Walker) {
    walker.walk();
}

// Main function
fn main() {
    let penguin = Penguin;
    let duck = Duck;
    let eagle = Eagle;

    swim(&penguin);
    swim(&duck);
    fly(&duck);
    fly(&eagle);
    walk(&eagle);
}

Open/Closed Principle

Software entities should be open for extension, but closed for modification.

// Define a trait `Shape` with a method `area`
pub trait Shape {
    fn area(&self) -> f64;
}

// Implement the `Shape` trait for a `Rectangle` struct
pub struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    pub fn new(width: f64, height: f64) -> Self {
        Self { width, height }
    }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

// Implement the `Shape` trait for a `Circle` struct
pub struct Circle {
    radius: f64,
}

impl Circle {
    pub fn new(radius: f64) -> Self {
        Self { radius }
    }
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

// A function that calculates the total area of a list of shapes
pub fn total_area(shapes: &[&dyn Shape]) -> f64 {
    shapes.iter().map(|shape| shape.area()).sum()
}

fn main() {
    let rectangle = Rectangle::new(5.0, 4.0);
    let circle = Circle::new(3.0);

    let shapes: Vec<&dyn Shape> = vec![&rectangle, &circle];

    println!("Total area: {}", total_area(&shapes));
}

Liskov Substitution Principle

Subtypes should be substitutable for their base types.

pub trait Bird {
    fn fly(&self);
}

pub struct Swallow {
    pub name: String,
}

impl Swallow {
    pub fn new(name: String) -> Self {
        Self { name }
    }
}

impl Bird for Swallow {
    fn fly(&self) {
        println!("{} the Swallow is flying.", self.name);
    }
}

pub struct Penguin {
    pub name: String,
}

impl Penguin {
    pub fn new(name: String) -> Self {
        Self { name }
    }
}

impl Bird for Penguin {
    fn fly(&self) {
        println!("{} the Penguin cannot fly.", self.name);
    }
}

pub fn let_birds_fly(birds: &[&dyn Bird]) {
    for bird in birds {
        bird.fly();
    }
}

fn main() {
    let swallow = Swallow::new(String::from("Jack"));
    let penguin = Penguin::new(String::from("Penny"));

    let birds: Vec<&dyn Bird> = vec![&swallow, &penguin];

    let_birds_fly(&birds);
}

Bash Utils

use std::env;
use std::fs;
use std::path::PathBuf;

fn main() {
    let home_dir = env::var("HOME").expect("Failed to get user's home directory");
    let downloads_dir = format!("{}/Downloads", home_dir);
    let pdf_dir = format!("{}/pdf", downloads_dir);

    // Create the directory if it doesn't exist
    if let Err(_) = fs::create_dir_all(&pdf_dir) {
        eprintln!("Failed to create directory: {}", pdf_dir);
        return;
    }

    let entries = match fs::read_dir(&downloads_dir) {
        Ok(entries) => entries,
        Err(e) => {
            eprintln!("Failed to read directory: {}\nError: {}", downloads_dir, e);
            return;
        }
    };

    let mut file_count = 0;
    let mut total_bytes_moved = 0;

    for entry in entries {
        if let Ok(entry) = entry {
            let metadata = match entry.metadata() {
                Ok(metadata) => metadata,
                Err(e) => {
                    eprintln!("Failed to get metadata: {}", e);
                    continue;
                }
            };

            if metadata.is_file() {
                let file_name = entry.file_name();
                let file_name_str = file_name.to_string_lossy();

                if file_name_str.contains("pdf") {
                    let source_path = entry.path();
                    let destination_path = PathBuf::from(&pdf_dir).join(file_name.clone());

                    match entry.metadata() {
                        Ok(metadata) => {
                            let file_size = metadata.len();

                            if let Err(e) = fs::rename(&source_path, &destination_path) {
                                eprintln!("Failed to move file: {}\nError: {}", file_name_str, e);
                            } else {
                                println!("Moved file: {}", file_name_str);
                                file_count += 1;
                                total_bytes_moved += file_size;
                            }
                        }
                        Err(e) => {
                            eprintln!(
                                "Failed to get metadata for file: {}\nError: {}",
                                file_name_str, e
                            );
                        }
                    }
                }
            }
        }
    }

    println!(
        "Moved {} file(s), total bytes: {}",
        file_count, total_bytes_moved
    );
}

Go and in Rust

So Go can do this... can Rust do this?

s := fmt.Sprintf("example: {}\n", "text")
fn main() {
    let s = format!("example: {}\n", "text");
    println!("{}", s);
}

Maps

Go Sample

package main

import "fmt"

func main() {
	m := map[string]string{
		"key1": "value1",
		"key2": "value2",
	}

	fmt.Println(m)
}

Rust Sample

use std::collections::HashMap;

fn main() {
    let mut m: HashMap<String, String> = HashMap::new();
    m.insert("key1".to_string(), "value1".to_string());
    m.insert("key2".to_string(), "value2".to_string());

    println!("{:?}", m);
}

Append

Go Sample

package main

import "fmt"

func main() {
	s := []string{}
	s = append(s,"one")
	s = append(s,"two")

	fmt.Println(s)
}

Rust Sample

fn main() {
    let mut s: Vec<String> = Vec::new();
    s.push("one".to_string());
    s.push("two".to_string());

    println!("{:?}", s);
}

Structs and Recievers

Go Sample

package main

import "fmt"

type K struct {
	name string
	age  float64
}

type S struct {
	Name string
	b    []K
}

func (s *S) Get(name string) float64 {
	for _, v := range s.b {
		if v.name == name {
			return v.age
		}

	}
	return -1
}

func (s *S) Put(name string, age float64) {
	if s.b == nil {
		s.b = make([]K, 0)

	}
	s.b = append(s.b, K{name, age})

}

func main() {
	s := &S{"Bob", nil}
	s.Put("Bob", 12)
	s.Put("Alice", 13)
	fmt.Println("bob's age: ", s.Get("Bob"))

	fmt.Println(s)

}

Rust Sample

#[derive(Debug)]
struct K {
    name: String,
    age: f64,
}

#[derive(Debug)]
struct S {
    name: String,
    b: Vec<K>,
}

impl S {
    fn new(name: &str) -> Self {
        S {
            name: name.to_string(),
            b: Vec::new(),
        }
    }

    fn get(&self, name: &str) -> f64 {
        for k in &self.b {
            if k.name == name {
                return k.age;
            }
        }
        -1.0
    }

    fn put(&mut self, name: &str, age: f64) {
        self.b.push(K {
            name: name.to_string(),
            age,
        });
    }
}

fn main() {
    let mut s = S::new("Bob");
    s.put("Bob", 12.0);
    s.put("Alice", 13.0);
    println!("bob's age: {:?}", s.get("Bob"));

    println!("{:?}", s);
}

What about Go's defer?

fn main() {
    defer(|| {
        println!("This message always gets printed last!");
    });

    panic!("Oops, something went wrong!");
}

fn defer<F: FnOnce() + 'static>(f: F) {
    let deferrer = Defer { f: Some(f) };
    std::mem::forget(deferrer);
}

struct Defer<F: FnOnce() + 'static> {
    f: Option<F>,
}

impl<F: FnOnce() + 'static> Drop for Defer<F> {
    fn drop(&mut self) {
        if let Some(f) = self.f.take() {
            let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
        }
    }
}

Raw Notes

Daily progress notes, in no particular order.

2023-03-04

Write a Rust program each day, for 100 days.


fn add(a:&mut i32, b: i32) -> i32 {
    *a += b;
    *a
}

fn main() {
    let mut x = 5;
    println!("{}\n", add(&mut x, 2));
    let mut y = add(&mut x, 20);
    println!("{}\n", add(&mut y, 2));    
}  

It's also possible to add Go code. But, this code won't execute.

package main

import "fmt"

func main() {
    fmt.Println("Hello, playground")
}

From and Into

#[derive(Debug)]
struct Meter(u32);

#[derive(Debug)]
struct Feet(u32);

// Implementing the From trait for converting Feet into Meter
impl From<Feet> for Meter {
    fn from(feet: Feet) -> Self {
        Meter((feet.0 as f32 * 0.3048) as u32)
    }
}

// Implementing the Into trait for converting Meter into Feet
impl Into<Feet> for Meter {
    fn into(self) -> Feet {
        Feet((self.0 as f32 * 3.28084) as u32)
    }
}

fn main() {
    // Using the From trait
    let feet = Feet(100);
    let meter: Meter = Meter::from(feet);
    println!("100 feet is equal to {:?} meters", meter);

    // Using the Into trait
    let meter = Meter(30);
    let feet: Feet = meter.into();
    println!("30 meters is equal to {:?} feet", feet);
}

Hashmap 1


use std::collections::HashMap;

fn create_hashmap() -> Box<HashMap<i32, String>> {
    let mut map = HashMap::new();
    map.insert(1, "hello".to_string());
    map.insert(2, "world".to_string());

    Box::new(map)
}

fn add_to_boxed_hashmap(box_map: &mut Box<HashMap<i32, String>>, key: i32, value: String) {
    for i in 0..10 {
        box_map.insert(i, format!("hello {}", i));
    }
    box_map.insert(key, value);
}

fn main() {
    let mut box_map = create_hashmap();
    add_to_boxed_hashmap(&mut box_map, 0, "foo".to_string());
    println!("{:?}", box_map);
}


map,filter_map, collect

fn main() {
    let names = vec!["Bob", "Frank", "Ferris"];

    let r = names
        .iter()
        .filter_map(|name| match name.starts_with("F") {
            true => Some(name),
            false => None,
        })
        .map(|name| name.to_uppercase())
        .collect::<Vec<String>>();

    println!("{:?}", r);
}

Here's a more complicated example. This is a common pattern in Rust.

fn modify_names(names: &mut Vec<&str>) {
    names.iter_mut().for_each(|i| {
        let new_name = match *i {
            "Bob" => format!("{}!, What's happening?", *i),
            _ => format!("Hi {}", *i),
        };
        *i = Box::leak(new_name.into_boxed_str());
    });
}

fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    modify_names(&mut names);

    println!("{:?}", names);
}

And you can add a custom message.

fn modify_names(names: &mut Vec<&str>,custom_message: &str) {
    names.iter_mut().for_each(|i| {
        let new_name = match *i {
            "Bob" => format!("{}!, What's happening?", *i),
            _ => format!("Hi {} {}", *i,custom_message),
        };
        *i = Box::leak(new_name.into_boxed_str());
    });
}

fn main() {
    let mut names = vec!["Bob", "Frank", "Ferris"];

    modify_names(&mut names,", custom message!");

    println!("{:?}", names);
}

Rust AWS SDK 1

See day29 for a complete example.

cargo add aws-sdk-sqs

References: https://crates.io/crates/aws-sdk-sqs

https://awslabs.github.io/aws-sdk-rust/

https://crates.io/

https://crates.io/crates/tokio

https://github.com/tokio-rs/mini-redis

[dependencies]
aws-config = "0.54.1"
aws-sdk-sqs = "0.24.0"
tokio = { version = "1", features = ["full"] }

Repos to look at

https://github.com/awslabs/aws-sdk-rust

https://awslabs.github.io/aws-sdk-rust/

References: https://crates.io/crates/aws-sdk-sqs

https://awslabs.github.io/aws-sdk-rust/

https://crates.io/

https://crates.io/crates/tokio

https://github.com/tokio-rs/mini-redis

100+ Questions

  • What is Rust and what makes it unique?

  • What are the advantages of using Rust over other languages?

  • Explain the concepts of ownership, borrowing, and lifetimes in Rust.

  • What is a variable shadowing in Rust?

  • What is the difference between mutable and immutable variables?

  • How is memory safety ensured in Rust?

  • What is the borrow checker and how does it work?

  • What are the differences between a slice and an array in Rust?

  • How do you define a function in Rust?

  • What is pattern matching and how is it used in Rust?

  • What is a tuple and how does it differ from an array?

  • What is the match expression and how does it work?

  • What are enums and how do you use them?

  • Explain the concept of structs in Rust.

  • How do you implement methods for a struct?

  • What are traits and how are they used in Rust?

  • What are the differences between a trait and an interface in other languages?

  • What is a module in Rust and how do you create one?

  • How do you handle errors in Rust?

  • Explain the difference between panic and Result.

  • What is the Option type and when should you use it?

  • What is an iterator in Rust and how do you create one?

  • How do you work with strings in Rust?

  • What is the difference between a String and a &str in Rust?

  • How do you implement generics in Rust?

  • What is a macro in Rust and how do you define one?

  • Explain the differences between macros and functions.

  • What is the standard library and what are some of its components?

  • How does Rust handle concurrency?

  • What is the difference between threads and tasks in Rust?

  • Explain the concept of async/await in Rust.

  • What is a closure and how does it work in Rust?

  • What are the differences between a closure and a function?

  • How do you work with files and directories in Rust?

  • Explain the concept of a Result type and its use cases.

  • How do you perform error propagation in Rust? -- What is the difference between a Vec and an array in Rust?

  • What is a HashMap and how do you use it in Rust?

  • Explain the concept of reference counting in Rust.

  • What are the differences between Arc and Rc?

  • What is a smart pointer and how does it work in Rust?

  • Explain the concept of interior mutability in Rust.

  • What are the differences between Cell and RefCell?

  • How does Rust handle null values?

  • What is the difference between PartialEq and Eq?

  • What is a type alias and how do you create one?

  • Explain the concept of associated types in Rust.

  • What is an associated function and how do you define one?

  • How do you implement multiple traits for a single type?

  • What is the purpose of the drop function in Rust?

  • Explain the concept of the Deref trait.

  • What is the purpose of the PhantomData type?

  • What is the difference between Send and Sync?

  • Explain the concept of unsafe code in Rust.

  • What is a raw pointer and how do you use it?

  • How do you define a constant in Rust?

  • What is a static variable and how do you define one?

  • What is a lazy_static and when should you use it?

  • How do you define and use custom error types in Rust?

  • What is the difference between Clone and Copy traits?

  • What is a lifetime and why is it important in Rust?

  • How do you specify lifetime annotations in Rust?

  • What is the 'static lifetime and when is it used?

  • What is the purpose of the Sized trait?

  • Explain the concept of type erasure in Rust.

  • How do you use cargo and what is its purpose?

  • How do you create and use a dependency in a Rust project?

  • What is a workspace in Cargo and how do you create one?

  • Explain the concept of conditional compilation in Rust.

  • What is a build script and how do you use it in a Rust project?

  • What is a feature flag and how do you use it in Rust?

  • Explain the concept of testing in Rust.

  • How do you write unit tests and integration tests in Rust?

  • What is the purpose of the #[cfg(test)] attribute?

  • How do you perform benchmarking in Rust?

  • What is the difference between a no_std and a std Rust project?

  • How do you compile Rust code to WebAssembly?

  • What is serde and how do you use it in Rust?

  • How do you implement serialization and deserialization in Rust?

  • Explain the concept of FFI (Foreign Function Interface) in Rust.

  • How do you call C code from Rust and vice versa?

  • What is the purpose of the Pin type in Rust?

  • Explain the concept of zero-cost abstractions in Rust.

  • What is the difference between a Box, a Rc, and an Arc?

Box: A Box provides heap allocation for a single value. It is useful when you want to allocate data on the heap rather than the stack. In this example, we create a Box containing a Foo instance and print its value.
Rc (Reference Counting): Rc is used when you want to share ownership of an instance across multiple parts of your code. It tracks the number of references to an instance and deallocates the memory when the reference count goes down to zero. In this example, we create an Rc containing a Foo instance, clone it, and print the value of both the original Rc and the clone.
Arc (Atomic Reference Counting): Arc is similar to Rc, but it is thread-safe, allowing shared ownership across multiple threads. It uses atomic operations to manage the reference count, which makes it safe to use concurrently. In this example, we create an Arc containing a Foo instance, clone it, spawn a new thread that uses the cloned instance, and print the value of both the original Arc and the clone in the new thread.
use std::rc::Rc;
use std::sync::Arc;
use std::thread;

struct Foo {
    value: i32,
}

fn main() {
    // Box
    let foo_box = Box::new(Foo { value: 42 });
    println!("Box value: {}", foo_box.value);

    // Rc (Reference Counting)
    let foo_rc = Rc::new(Foo { value: 42 });
    let foo_rc_clone = Rc::clone(&foo_rc);
    println!("Rc value: {}", foo_rc.value);
    println!("Rc clone value: {}", foo_rc_clone.value);

    // Arc (Atomic Reference Counting)
    let foo_arc = Arc::new(Foo { value: 42 });
    let foo_arc_clone = Arc::clone(&foo_arc);

    let handle = thread::spawn(move || {
        println!("Arc value in thread: {}", foo_arc_clone.value);
    });

    println!("Arc value: {}", foo_arc.value);

    handle.join().unwrap();
}

  • What is a Mutex and how do you use it in Rust?
  • What is a RwLock and how do you use it in Rust?
  • How do you implement a custom iterator in Rust?
  • What is the purpose of the From and Into traits?
#[derive(Debug)]
struct Meter(u32);

#[derive(Debug)]
struct Feet(u32);

// Implementing the From trait for converting Feet into Meter
impl From<Feet> for Meter {
    fn from(feet: Feet) -> Self {
        Meter((feet.0 as f32 * 0.3048) as u32)
    }
}

// Implementing the Into trait for converting Meter into Feet
impl Into<Feet> for Meter {
    fn into(self) -> Feet {
        Feet((self.0 as f32 * 3.28084) as u32)
    }
}

fn main() {
    // Using the From trait
    let feet = Feet(100);
    let meter: Meter = Meter::from(feet);
    println!("100 feet is equal to {:?} meters", meter);

    // Using the Into trait
    let meter = Meter(30);
    let feet: Feet = meter.into();
    println!("30 meters is equal to {:?} feet", feet);
}
  • What is the difference between a ref and a ref mut in Rust?
  • How do you use conditional statements in Rust?
  • What are the different types of loops in Rust and how do you use them?
  • Explain the concept of a tuple struct in Rust.
  • How do you implement a recursive function in Rust?
  • What is the difference between a function and a method in Rust?
  • What is a constructor and how do you create one in Rust?
  • Explain the concept of type inference in Rust.
  • What is the purpose of the AsRef and AsMut traits?
  • What is a nightly build of Rust and when should you use it?
  • What are the different editions of Rust and how do they differ?
  • What resources do you recommend for learning Rust?

Videos

Island Problem