Commit 805f17a4 authored by Chris's avatar Chris
Browse files

[references-and-borrowing] section complete

parent f703e8b7
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4

[[package]]
name = "references-and-borrowing"
version = "0.1.0"
+6 −0
Original line number Diff line number Diff line
[package]
name = "references-and-borrowing"
version = "0.1.0"
edition = "2024"

[dependencies]
+92 −0
Original line number Diff line number Diff line
// this code won't compile because once m1 and m2 are passed into `greet()`,
//      they are borrowed and not returned back
// fn main() {
//     let m1 = String::from("Hello");
//     let m2 = String::from("world");
//     greet(m1, m2);
//     let s = format!("{} {}", m1, m2); // Error: m1 and m2 are moved
// }
//
// fn greet(g1: String, g2: String) {
//     println!("{} {}!", g1, g2);
// }

// instead, do this:
fn main() {
    let m1 = String::from("Hello");
    let m2 = String::from("world");
    greet(&m1, &m2);
    let s = format!("{} {}", m1, m2);
    println!("{s} (again)");

    // these are the inner workings of pointers and dereferencing
    let mut x: Box<i32> = Box::new(1);
    let _a: i32 = *x;         // *x reads the heap value, so a = 1
    *x += 1;                 // *x on the left-side modifies the heap value,
    //     so x points to the value 2

    let r1: &Box<i32> = &x;  // r1 points to x on the stack
    let _b: i32 = **r1;       // two dereferences get us to the heap value

    let r2: &i32 = &*x;      // r2 points to the heap value directly
    let _c: i32 = *r2;    // so only one dereference is needed to read it

    // dereference operator can be implicitly declared in certain cases
    let x: Box<i32> = Box::new(-1);
    let x_abs1 = i32::abs(*x); // explicit dereference
    let x_abs2 = x.abs();      // implicit dereference -- dot operator
    assert_eq!(x_abs1, x_abs2);

    let r: &Box<i32> = &x;
    let r_abs1 = i32::abs(**r); // explicit dereference (twice)
    let r_abs2 = r.abs();       // implicit dereference (twice)
    assert_eq!(r_abs1, r_abs2);

    let s = String::from("Hello");
    let s_len1 = str::len(&s); // explicit reference
    let s_len2 = s.len();      // implicit reference
    assert_eq!(s_len1, s_len2);

    // using a bunch of dereferences to get values
    let x = Box::new(12);
    let y = Box::new(&x);
    let z = ***y + 1;
    println!("{z}");

    // here's vectors
    let mut v: Vec<i32> = vec![1, 2, 3]; // macro vec![] creates a vector with the elements inside the []
                                         //     also only allocates a heap array of a certain capacity
                                         //     meaning: when you do a push(), we get new allocation
                                         //     and copy all elements over, with alloc. dealloc. process
    v.push(4);

    // you can't use num when you push() (uncomment to see how)
    // let mut v_again: Vec<i32> = vec![1, 2, 3];
    // let num: &i32 = &v_again[2];
    // v_again.push(4);
    // println!("Third element is {}", *num);
    // principle is that data can be aliased or mutated, but not both

    // ... but due to the nature of variable ownership, you can do something like this:
    let mut v_again: Vec<i32> = vec![1, 2, 3]; // v_again has RWO permissions on its data
    let num: &i32 = &v_again[2];               // v_again loses W permissions, num has RO permissions, and *num has R permissions
    println!("Third element is {}", *num);     // num is freed, and v_again gains back its WO permissions
    v_again.push(4); // perfectly legal

    let mut ve: Vec<i32> = vec![1, 2, 3]; // ve has RWO
    let num: &mut i32 = &mut ve[2]; // ve loses RWO, num gains RO, *num gains RW. see how the value gets borrowed?
    *num += 1; // *num is mutated (which is a pointer to a value in ve)
    println!("Third element is {}", *num); // *num still has read ownership and is freed
    println!("Vector is now {:?}", ve); // ve is read from and freed

    let mut x = 1; // x has RWO
    let y = &x;   // x now only has R: y gains RO of x's value, and *y has R
    let z = *y;    // x regains RWO, y freed, z has RO
    x += z;             // x is freed, z is freed
    println!("{x}");
}

fn greet(g1: &String, g2: &String) {
    println!("{} {}!", g1, g2);
}