Data Types

Note: Rust is a statically typed language. This means that the type of a variable must be known at compile time. This is in contrast to dynamically typed languages like Python, where the type of a variable is determined at runtime.

  1. bool: A boolean value that can be either true or false
let is_true: bool = true;
  1. char: A single Unicode character.
let c: char = 'a';
  1. i8, i16, i32, i64, i128: Signed integers with different sizes
let x: i32 = -69;
  1. u8, u16, u32, u64, u128: Unsigned integers with different sizes
let x: u32 = 420;
  1. f32, f64: Floating-point numbers with different precisions.
let x: f32 = 69.420;
  1. ‘array’: A fixed-size array of elements of the same type.
let a: [i32; 3] = [1, 2, 3];
  1. Vec: A growable array of elements of the same type.
let v: Vec<i32> = vec![1, 2, 3];
  1. usize, isize: The pointer-sized unsigned and signed integers.
let x: usize = 420;
  1. tuple: An ordered collection of elements of different types
let t: (i32, f32, char) = (69, 420.69, 'a');
  1. &str: A string slice that points to a valid UTF-8 string stored elsewhere.
let s: &str = "Hello World";
  1. String: A heap-allocated string, growable at runtime.
let s: String = String::from("Hello World");

Variables

  1. Declaring a variable with the let keyword
let x = 69;
  1. Variables are immutable by default, but you can make them mutable with the mut keyword.
let mut x = 69;
x = 420;
  1. You can also declare multiple variables with the same type.
let (x, y, z): (i32, i32, i32) = (1, 2, 3);
  1. Rust has block-scoped variables. This means that variables declared inside a block are only accessible inside that block.
let x = 42;
{
    let x = 43;
    println!("{}", x); // prints 43

    let y = 69;
}
println!("{}", x); // prints 42
println!("{}", y); // error: y is not in scope
  1. You can also declare constants with the const keyword. Constants are always immutable and must be annotated with their type. Constants can be declared in any scope, including the global scope.
const PI: f32 = 3.14159265359;

Shadowing

  1. You can declare a variable with the same name as a previous variable, and the new variable shadows the previous variable.
let x = 69;
let x = 420;
  1. You can also change the type of a variable when you shadow it.
let x = 69;
let x = "Hello World";

Control Flow

  1. if statements can be used to branch on a condition
let x = 42;
if x == 42 {
    println!("The answer is {}", x);
} else {
    println!("The answer is not {}", x);
}
  1. if statements can also be used as expressions
let x = if true { 69 } else { 420 };
  1. loop statements can be used to create infinite loops
let mut i = 0;
loop {
    print!("{} ", i);
    i += 1;
    if i == 10 {
        break;
    }
}
  1. while statements can be used to repeat a block of code while a condition is true
let mut i = 0;
loop {
    println!("{}", i);
    i += 1;
    if i == 10 {
        break;
    }
}
  1. for statements can be used to iterate over a collection
for i in 0..10 {
    println!("{}", i);
}

let a = [1, 2, 3];

for i in 0..a.len() {
    println!("{}", a[i]);
}

for i in &a {
    println!("{}", i);
}

for i in a.iter() {
    println!("{}", i);
}

// Reverse iteration
for i in (0..10).rev() {
    println!("{}", i);
}

for i in (0..10).step_by(2) {
    println!("{}", i);
}

// Filtered iteration
// Equivalent to: for i in 0..10 { if i % 2 == 0 { println!("{}", i); } }
// Note: filter() is a method on the Iterator trait
// Explanation: Only even numbers are passed to the closure, which is then passed to the filter() method
for i in (0..10).filter(|x| x % 2 == 0) {
    println!("{}", i);
}
  1. match statements can be used to match a value against a pattern
let x = 42;
match x {
    0 => println!("Zero"),
    1 => println!("One"),
    _ => println!("Something else"),
}

Output: Something else

  1. match statements can also be used as expressions
let x = match 42 {
    0 => "Zero",
    1 => "One",
    _ => "Something else",
};

println!("{}", x);

Output: Something else

  1. if let statements can be used to match a value against a pattern and bind the value to a variable
let x = Some(42);
if let Some(i) = x {
    println!("{}", i);
} else {
    println!("No value");
}

Output: 42


Functions

  1. Declaring a function with the fn keyword
fn add(x: i32, y: i32) -> i32 {
    x + y
}

Note: The -> i32 part of the function signature is the return type. If the function doesn’t return a value, the return type is written as -> ().

  1. Rust has expressions and statements, and functions can return expressions.
// This function returns an expression
fn add(x: i32, y: i32) -> i32 {
    x + y
}

// This function returns a statement
fn print(x: i32) {
    println!("{}", x);
}
  1. You can also declare functions inside other functions.
fn main() {
    fn add(x: i32, y: i32) -> i32 {
        x + y
    }
}