Rust cheat-sheet

In the following sections, I’ll describe the basics of Rust programming language with Q/A flashcards based on Learn Rust in Y Minutes 1 cheatcheet.

// This is a comment. Line comments look like this...
// and extend multiple lines like this.
 
/* Block comments
  /* can be nested. */ */
 
/// Documentation comments look like this and support markdown notation.
/// # Examples
///
/// let five = 5
fn main() {}

1. Basics

#[allow(dead_code)]
// Functions
// `i32` is the type for 32-bit signed integers
fn add2(x: i32, y: i32) -> i32 {
    // Implicit return (no semicolon)
    x + y
}
fn main() {
  println!("2 + 2 = {}", add2(2, 2));
}

What is the output of the above code?
Results: 2 + 2 = 4

How to set immutable variables?

fn main() {
    let x: i32 = 1;
    println!("Immutable binding (variables): {}", x);
 
    let y: i32 = 13i32;
    let f: f64 = 1.3f64;
    println!("Integer/float numbers suffixes: {} {}", y, f);
}

Is there type inference in Rust?
Yes. Most of the time, the Rust compiler can infer what type a variable is, so you don’t have to write an explicit type annotation.

use std::any::type_name;
 
fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}
 
fn main() {
    let x = 1;
    let y = 1.3;
    println!("Types of 1 and 1.3:");
    print_type_of(&x);
    print_type_of(&y);
}

Results:

Types of 1 and 1.3:
i32
f64

How to sum two bindings, 1 and 2?
Need just to use + operator.

fn main() {
    let x = 1;
    let y = 2;
    let sum = x + y + 13;
    println!("Sum of 1, 2, 13 is {}", sum);  // Sum of 1, 2, 13 is 16
}

How to set and change mutable variable?

fn main() {
    // Mutable variable
    let mut mutable = 1;
    mutable = 4;
    mutable += 2;
    println!("{}", mutable); // 6
}

How to set string literals?

fn main(){
    // String literals
    let x: &str = "hello world!";
    let f = 1.3;
 
    // Printing
    println!("{} {}", f, x);  // 1.3 hello world!
}

How to set a heap-allocated string (dynamic string)?

fn main() {
    // A `String` – a heap-allocated string
    // Stored as a `Vec<u8>` and always holds a valid UTF-8 sequence,
    // which is not null terminated.
    let s: String = "hello world".to_string();
}

A string slice – an immutable view into another string. This is basically an immutable pointer and length of a string – it doesn’t actually contain the contents of a string, just a pointer to the beginning and a length of a string buffer, statically allocated or contained in another object (in this case, s). The string slice is like a view &[u8] into Vec<T>.

fn main() {
    let s: String = "hello world".to_string();
    let s_slice: &str = &s;
 
    println!("{} {}", s, s_slice);
}

What this code will print?
Results: hello world hello world

How to set a fixed-size array and dynamic array (vector)?

fn main() {
    // A fixed-size array
    let four_ints: [i32; 4] = [1, 2, 3, 4];
 
    // A dynamic array (vector)
    let mut vector: Vec<i32> = vec![1, 2, 3, 4];
    vector.push(5);
 
    println!("Fixed-size array {:?}", four_ints);
    println!("Dynamic array {:?}", vector);
}

Results:

Fixed-size array [1, 2, 3, 4]
Dynamic array [1, 2, 3, 4, 5]

A slice – an immutable view into a vector or array. This is much like a string slice, but for vectors. How to create slice of vec![1, 2, 3, 4]?

fn main() {
    let mut vector: Vec<i32> = vec![1, 2, 3, 4];
    let slice: &[i32] = &vector[0..2];
 
    // Use `{:?}` to print something debug-style
    println!("{:?} {:?}", vector, slice); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
}

A tuple is a fixed-size set of values of possibly different types. How to define tuple in rust with 1, "hello", 3.4 items, how to get second item?

fn main() {
    let x: (i32, &str, f64) = (1, "hello", 3.4);
 
    // Indexing
    println!("{}", x.1); // hello
}

How to destructure (unpacking with strict arity) let x: (i32, &str, f64) into a, b, c?

// Destructuring `let`
let (a, b, c) = x;
println!("{} {} {}", a, b, c); // 1 hello 3.4

2. Types

How to define a struct with x, y (i32) fields?

fn main() {
    // Struct
    struct Point {
        x: i32,
        y: i32,
    }
 
    let origin: Point = Point { x: 0, y: 0 };
    println!("Origin is at ({}, {})", origin.x, origin.y);
}

A struct with unnamed fields, called a ‘tuple struct’, how to define it?

fn main() {
    struct Point2(i32, i32);
 
    let origin2 = Point2(0, 0);
    println!("Origin is at ({}, {})", origin2.0, origin2.1);
}

How to define basic C-like enum?

fn main() {
    enum Direction {
        Left,
        Right,
        Up,
        Down,
    }
 
    let up = Direction::Up;
}

How to create enum with fields?
Enum with fields. If you want to make something optional, the standard library has Option.

fn main() {
    enum OptionalI32 {
        AnI32(i32),
        Nothing,
    }
 
    let two: OptionalI32 = OptionalI32::AnI32(2);
    let nothing = OptionalI32::Nothing;
}

When these generics are useful?

struct Foo<T> { bar: T }
 
// This is defined in the standard library as `Option`
// `Option` is used in place of where a null pointer
// would normally be used.
enum Optional<T> {
    SomeVal(T),
    NoVal,
}
 
fn main() {
    // Using Foo<T> with different types
    let foo_int = Foo { bar: 42 };
    let foo_str = Foo { bar: "Hello, Rust!" };
 
    println!("Foo with i32: {}", foo_int.bar);
    println!("Foo with &str: {}", foo_str.bar);
 
    // Using Optional<T> (custom Option<T>)
    let some_value: Optional<i32> = Optional::SomeVal(100);
    let no_value: Optional<i32> = Optional::NoVal;
 
    // Matching on Optional<T>
    match some_value {
        Optional::SomeVal(v) => println!("We have a value: {}", v),
        Optional::NoVal => println!("No value present"),
    }
    // Foo with i32: 42
    // Foo with &str: Hello, Rust!
 
    match no_value {
        Optional::SomeVal(v) => println!("We have a value: {}", v),
        Optional::NoVal => println!("No value present"),
    }
    // We have a value: 100
    // No value present
}

We use generics to create **definitions** for items like function signatures or structs, which we can then use with **many different** concrete data types. They also allow us to avod code duplication.

Using methods with generics.

struct Foo<T> { bar: T }
 
fn main() {
    impl<T> Foo<T> {
        // Methods take an explicit `self` parameter
        fn bar(&self) -> &T { // self is borrowed (immutable)
            &self.bar
        }
        fn bar_mut(&mut self) -> &mut T { // self is mutably borrowed
            &mut self.bar
        }
        fn into_bar(self) -> T { // here self is consumed
            self.bar
        }
    }
 
    let a_foo = Foo { bar: 1 };
    let mut b_foo = Foo { bar: 2 };
    let c_foo = Foo { bar: 3 };
    println!("{}", a_foo.bar());
    println!("{}", b_foo.bar_mut());
    println!("{}", c_foo.into_bar());
}

What this code will print?
Results:

1
2
3

Traits, known as interfaces or typeclasses in other languages. Functionality a particular type has and can share with other types. How to define trait?

struct Foo<T> { bar: T }
 
fn main() {
    trait Frobnicate<T> {
        fn frobnicate(self) -> Option<T>;
    }
 
    impl<T> Frobnicate<T> for Foo<T> {
        fn frobnicate(self) -> Option<T> {
            Some(self.bar)
        }
    }
 
    let another_foo = Foo { bar: 1 };
    println!("{:?}", another_foo.frobnicate()); // Some(1)
}

How to define function pointer and it’s type?

fn main() {
    // Some function
    fn fibonacci(n: u32) -> u32 {
        match n {
            0 => 1,
            1 => 1,
            _ => fibonacci(n - 1) + fibonacci(n - 2),
        }
    }
 
    // Define function pointer type
    type FunctionPointer = fn(u32) -> u32;
 
    let fib : FunctionPointer = fibonacci;
    println!("Fib: {}", fib(4)); // 5
    println!("Pointer address: {:?}", fib as *const u32);
}

Results:

Fib: 5
Pointer address: 0x55b7c5107c60

3. Pattern matching

How to match on enum?

enum OptionalI32 {
    AnI32(i32),
    Nothing,
}
 
fn print_nothing() {
    println!("it’s nothing!")
}
 
fn main() {
    let foo = OptionalI32::AnI32(1);
    match foo {
        OptionalI32::AnI32(n) => println!("it’s an i32: {}", n),
        OptionalI32::Nothing  => print_nothing(),
    }
    // it’s an i32: 1
}

What this code will print?

enum OptionalI32 {
    AnI32(i32),
    Nothing,
}
 
fn main() {
    struct FooBar { x: i32, y: OptionalI32 }
    let bar = FooBar { x: 15, y: OptionalI32::AnI32(32) };
    match bar {
        FooBar { x: 0, y: OptionalI32::AnI32(0) } =>
            println!("The numbers are zero!"),
        FooBar { x: n, y: OptionalI32::AnI32(m) } if n == m =>
            println!("The numbers are the same"),
        FooBar { x: n, y: OptionalI32::AnI32(m) } =>
            println!("Different numbers: {} {}", n, m),
        FooBar { x: _, y: OptionalI32::Nothing } =>
            println!("The second number is Nothing!"),
    }
}

_Results:_ `Different numbers: 15 32`

4. Control flow

How to use simple control flow?

fn main() {
    if 1 == 1 {
        println!("Maths is working!"); // Maths is working!
    } else {
        println!("Oh no...");
    }
}

How to use if as expression of let value?

fn main() {
    let value = if true {
        "good"
    } else {
        "bad"
    };
 
    println!("{}", value); // good
}

How to iterate over [1, 2, 3], 0u32..10 and print each item?

fn main() {
    // `for` loops/iteration
    let array = [1, 2, 3];
    for i in array {
        println!("{}", i);
    }
 
    // Ranges
    for i in 0u32..10 {
        print!("{} ", i);
    }
    // prints `0 1 2 3 4 5 6 7 8 9 `
}

How to create loop with while and loop keywords?

fn main() {
    while 1 == 1 {
        println!("The universe is operating normally.");
        // break statement gets out of the while loop.
        // It avoids useless iterations.
        break;
    }
 
    // Infinite loop if `break` is not used
    loop {
        println!("Hello!");
        // break statement gets out of the loop
        break;
    }
}

5. Memory safety & pointers

What last line of this code will print?

fn main() {
    // Owned pointer – only one thing can ‘own’ this pointer at a time
    // This means that when the `Box` leaves its scope, it will be automatically deallocated safely.
    let mut mine: Box<i32> = Box::new(3);
    *mine = 5; // dereference
 
    // Here, `now_its_mine` takes ownership of `mine`. In other words, `mine` is moved.
    let mut now_its_mine = mine;
    *now_its_mine += 2;
 
    println!("{}", now_its_mine); // 7
    println!("{}", mine); // << ???
}

This would not compile (error: borrow of moved value `mine`), because `now_its_mine` now owns the pointer

Reference – an immutable pointer that refers to other data When a reference is taken to a value, we say that the value has been ‘borrowed’. While a value is borrowed immutably, it cannot be mutated or moved. A borrow is active until?
The last use of the borrowing variable.

fn main() {
    let mut var = 4;
    var = 3;
    let ref_var: &i32 = &var;
 
    println!("{}", var); // Unlike `mine`, `var` can still be used
    println!("{}", *ref_var);
    // var = 5; // this would not compile because ==`var` is borrowed==
    // *ref_var = 6; // this would not either, because `ref_var` is an immutable reference
    ref_var; // no-op, but counts as a use and keeps the borrow active
    var = 2; // ref_var is no longer used after the line above, so the borrow has ended
}

While a value is mutably borrowed, it cannot be accessed at all, is that true?
Yes. For example:

fn main() {
    // Mutable reference
    // While a value is mutably borrowed, it cannot be accessed at all.
    let mut var2 = 4;
    println!("{}", var2);
 
    let ref_var2: &mut i32 = &mut var2;
    *ref_var2 += 2;         // '*' is used to point to the mutably borrowed var2
 
    println!("{}", *ref_var2); // 6 , // var2 would not compile.
    // ref_var2 is of type &mut i32, so stores a reference to an i32, not the value.
    // var2 = 2; // WARN: this would not compile because `var2` is borrowed.
    ref_var2;    // no-op, but counts as a use and keeps the borrow active until here
    // var2 = 2;
  }

NEXT: compare with original cheatcheet.

Footnotes

  1. Learn Rust in Y Minutes