Table of Contents

Rust

Resources

General

Rust uses snake case convention:

Printing data structures is possible using annotations

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
 
let rect1 = Rectangle {
    width: 30,
    height: 30,
};
println!("{:?}", rect1); // inline
println!("{:#?}", rect1); // multiline

Attributes

#[allow(unused_variables, unused_mut)]
#[allow(unused)]
#[allow(dead_code)]
#[macro_use] // allows to use macros from extern crate

Attribute at a crate level

#![allow(unused)]

Cargo

cargo new PROJECT_NAME (--bin/lib)
cargo build --release
cargo run
cargo update
cargo doc --open

Diesel

Docs

CLI comands

cargo install diesel_cli
diesel setup
diesel migration generate create_users
diesel migration run
diesel migration redo
diesel print-schema > src/schema.rs
#[derive(Queryable)]
pub struct UserModel {
...
}

Data Types

Integers

(signed i8, unsigned u8) (isize defaults to your computer architecture 32/64 bit)

Floating-Point

Character

Tuple

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;
    let five_hundred = tup.0;
}

Array

let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5]; // let a = [3, 3, 3, 3, 3];
let a = [1, 2, 3, 4, 5];
let index = 10;
llet element = a[index]; //runtime error. Rust does not allow to access memory

Functions

Function bodies are made of statements and optionally end with an expression.

fn five() -> i32 {
    5
}
 
fn main() {
    let x = five();
 
    println!("The value of x is: {}", x);
}

Tuples

let rect1 = (30, 50);
 
fn area(dimensions: (u32, u32)) -> u32 {
    dimensions.0 * dimensions.1
}

Control Flow

Rust does not transform to bools automatically

let x = 5;
if x {} // error
if x != 0 {} // correct

Condition in let statement

let condition = true;
let number = if condition {
    5
} else {
    6
};
 

Rust has 3 types of loops: loop, while, for

Loop

Executes until you explicitly tell it to stop

loop {
    println!("again!");
}
fn main() {
    let mut counter = 0;
 
    let result = loop {
        counter += 1;
 
        if counter == 10 {
            break counter * 2;
        }
    };
 
    println!("The result is {}", result);
}

While

fn main() {
    let mut number = 3;
 
    while number != 0 {
        println!("{}!", number);
 
        number -= 1;
    }
 
    println!("LIFTOFF!!!");
}

For

fn main() {
    let a = [10, 20, 30, 40, 50];
 
    for element in a.iter() {
        println!("the value is: {}", element);
    }
}

Structs

Struct instances are mutable.

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
 
let user1 = User {
    username: String::from("A"),
    email: String::from("a@a.a"),
    active: true,
    sign_in_count: 1,
};
 
let user2 = {
    username: String::from("B"),
    ..user1
}

Tuple Structs

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
 
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

Implementing methods

impl Rectangle {
    fn area(&self) -> u32 {
        self.height * self.height
    }
}
 
let area = rect1.area();

Implementing namespaces

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}
let rect3 = Rectangle::square(30);

Collections

Vector

let v: Vec<i32> = Vec::new();
let v = vec![1,2,3,4,5];
 
let third: &i32 = &v[2]; //crashes for non-existent index
 
match v.get(2) { // can handle non-existent index
    Some(el) => println!("Third elem is: {}", el),
    None => println!("There is no third elem."),
}

To change the value of the vector in the loop we have to dereference it

let mut v = vec![100, 32, 57];
for i in &mut v {
    *i += 50;
}

Closures

let my_closure = |x| { x+1 };
my_closure(5); // types of closure automatically inferred by the compiler
 
let my_closure2 = |x: u32| -> u32 { x +1}; // explicit type annotations

Memoization / lazy evaluation

Technique where we execute expensive closure and hold resulting value for the future so we would not need to evaluate the closure again.

struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}
 
impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }
    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}
fn main() {
    let mut my_closure = Cacher::new(|x: u32| x + 1);
    println!("{}", my_closure.value(10)); // calculates for the first time
    println!("{}", my_closure.value(10)); // uses stored value
}