Loading projects/references-and-borrowing/Cargo.lock 0 → 100644 +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" projects/references-and-borrowing/Cargo.toml 0 → 100644 +6 −0 Original line number Diff line number Diff line [package] name = "references-and-borrowing" version = "0.1.0" edition = "2024" [dependencies] projects/references-and-borrowing/src/main.rs 0 → 100644 +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); } Loading
projects/references-and-borrowing/Cargo.lock 0 → 100644 +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"
projects/references-and-borrowing/Cargo.toml 0 → 100644 +6 −0 Original line number Diff line number Diff line [package] name = "references-and-borrowing" version = "0.1.0" edition = "2024" [dependencies]
projects/references-and-borrowing/src/main.rs 0 → 100644 +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); }