Skip to content

Instantly share code, notes, and snippets.

@hyojjxipitug
Last active March 3, 2026 11:24
Show Gist options
  • Select an option

  • Save hyojjxipitug/34e7eb3863e6cbca79acca7336d25b27 to your computer and use it in GitHub Desktop.

Select an option

Save hyojjxipitug/34e7eb3863e6cbca79acca7336d25b27 to your computer and use it in GitHub Desktop.

Rust personal CheatSheet

TODO: recap early sessions in the training

Intro

This document lists my personal notes for further reference taken during my Rust training on Ûdemy (Link)

Variable types

TODO complete this section

  • debug formatting: println!("{my_value:?}")

Integer

  • stored on the stack
  • implement copy trait, therefore the following creates two different values on the stack
let a: i32 = 5;
let mut b: i32 = a; // copies the original, we have now two different variables stored on the stack
b += 5;

println!("{a} {b}");

String vs. str

  • String - A dynamic piece of text stored on the heap at runtime

  • str - A hardcoded, read-only piece of text encoded in the binary

  • do not mistake &str

  • uses the String namespace

  • is stored on the heap

  • only a reference (a pointer) is stored in the stack

  • allocates some extra memory that can be used during string manipulation

let one: String = String::new();
let mut two: String = String::from("Two");
two.push_str(" Three");
println!("{two}");

Ownership

Base

  • a variable is more appropriately called a binding in Rust
  • The binding linked to a value is responsible for the clean up of the value upon leaving its scope
  • There can be only one owner at a time
  • the drop function is called upon cleaning of variable, i.e. when it goes out of scope. This only works for heap allocated variables
  • the clone method allows deep copy of object when allocated on the heap
  • you cannot take ownership to a value stored in another structure like an array or a tuple as it expects to remain owner of it. For example, the following is not allowed:
fn main() {
    let my_array = [String::from("one"), String::from("two"), String::from("three")];
    let a = my_array[0]; // not allowed; either borrow a reference or make a copy with clone()
}

Move

  • transfer of ownership from one owner to another
  • transfer happens if when copying variable that doesn't implement the copy trait. For example a String stored on the heap:
let person = String::from("Ben");
let employee = person; // the ownership has been transfered to the employee variable 

Borrowing & Reference

  • have a reference to a value without ownership
  • use the borrow operator &
fn main() {
    let my_stack_value: i32 = 2;
    let my_int_ref: &i32 = &my_stack_value; // note the data type

    let heap_value: String = String::from("horse");
    let heap_reference: &String = &heap_value;
}
  • reference like these are guaranteed to always be valid (References never outlive their referent)
  • de-reference operator: * (accesses the data at the reference)
let heap_reference: &String = &heap_value;
println!("{}", *heap_reference);
  • Rust ensures that references implement the display traits by using the display trait of the actual value behind the reference
  • The immutable references (not the mutable to avoid breaking the rule of single mutable reference to a value at the same time) implement the copy trait
let ref1: &str = "This is a test";
let ref2: &str = ref1; // we have now two different references on the stack pointing to the same encoded string in the binary
println!("{ref1} {ref2}");
  • a mutable reference is automatically dereferenced when useed:
  let mut value = String::from("test");
  let ref = &mut value;
  ref.push_str(" extension");
  • to borrow a mutable reference, the original variablee must also be mutable
  • there can be multiple immutable reference to a same value at the same time
  • However there can only be one single mutable reference to a value at a time. After a mutable reference has been created, it is not permitted to create even an immutable reference anymore
    • small caveat: the compiler is smart enough to detect if the mutable reference not used after some point and thereefore allows the creation of an immutable ref
  • a dangling reference is a reference to a value that has been cleared/deallocated. This is forbideen by the compiler. For example, the following is not allowed:
fn main() {}

fn create_city() -> &String {
    let city = String::from("Berlin");
    return &city;
}

Function parameters

  • by default, parameters are immutables. To change this:
fn main() {
    let my_string = String::from("This is a test");
    print_my_string(my_string);
}

fn print_my_string(mut value: String) {
    value.push_str(" [String extension]");
    println!("Your value is: {value}");
}
  • depending on the datatype, a function parameter will use a copy of the variable passed (if it implements the copy trait) or will move ownership
fn main() {
    let my_value: i32 = 155;
    print_my_value(my_value); // in this case the value is copied
    
    let my_string = String::from("This is a test");
    print_my_string(my_string); // ownership moved from the variable to the parameter.
    println!("my_string is not valid anymore: {my_string}");
}

fn print_my_value(value: i32) {
    println!("Your value is {value}");
}

fn print_my_string(value: String) {
    println!("Your value is {value}");
}
  • To only borrow a reference, change parameter declaration like this:
fn show_meal(meal: &String) { // fn show_meal(meal: &mut String) {...} if you want to modify it along the way
    println!("Meal: {meal}");
}

Return values

  • moves ownership from the function local variable to the caller via the return (implicit or explicit)
fn main() {
    let cake = bake_cake();
    println!("I have a cake: {cake}");
}

fn bake_cake() -> String {
    // let cake = String::from("Chocolate mousse");
    // return cake;
    
    String::from("Chocolate mousse")
}

Slices

  • a slice is a reference to a portion/sequence of a collection (e.g. array) up to 100% of the collection
fn main() {
    let name: String = String::from("Hyojj Xipitug");
    let firstname: &str = &name[0..5]; // From index 0 included up to index 5 NOT included

    println!("{firstname}");
}
  • slice boundaries can be skipped if we want to start fro, the beginning and/or finish at the end
fn main() {
    let action_hero = String::from("Arnold Schwarzenegger");

    let first_name = &action_hero[..6];
    println!("His first name is {first_name}.");
    
    let last_name = &action_hero[7..];
    println!("His last name is {last_name}.");
    
    let full_name = &action_hero[..];
    println!("His full name is {full_name}.");
}
  • deref coercion is the mechanism that Rust uses to deref an argument as long as possible until it reaches a valid type. This makes the following code valid:
fn do_hero_stuff(hero_name: &str) { // &String -> String -> &str
    println!("{hero_name} saves the day!");
}

fn main() {
    let action_hero = String::from("Arnold Schwarzenegger");
    do_hero_stuff(&action_hero); //deref coercion

    let another_hero = "Sylvester Stallone";
    do_hero_stuff(another_hero);
}
  • It is therefore more flexible to have &str as argument type
  • same logic of slicing with arrays:
fn main() {
    let values: [i32; 6] = [4, 8, 15, 16, 23, 42];

    let my_slice: &[i32] = &values[0..3]; // note that the type doesn't mention the length here
    println!("{my_slice:?}");
}
  • using slice allows more flexibility than the stricter reference:
fn main() {
    let values: [i32; 6] = [4, 8, 15, 16, 23, 42];

    let my_slice: &[i32] = &values[..]; // compare this type... (array slice)
    println!("{my_slice:?}");
    
    let my_slice: &[i32; 6] = &values; // ...with this type (reference to a full array, length included)
    println!("{my_slice:?}");
}
  • Rust does not allow mutable slice of string but well of arrays
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment