Daily Learnings: Fri, Nov 03, 2023
When we are no longer able to change a situation - we are challenged to change ourselves. — Viktor Frankl
Rust Study
During lunch today I was able to dive back into the Rust YouTube course that I’ve been slowly working through and learn some new things regarding examples of heap vs. stack memory management, and the concept of ownership and cloning vs. copying in Rust.
String vs. &str
We haven’t gotten far enough in the course yet to really understand this concept, and apparently the course will cover it later, but today I learned a tiny bit about the difference between the String type and the &str type.
String: This is the heap-memory-based type for storing String variables whose size is not known at compile-time, but will instead be determined at run-time&str: The is the stack-memory-based type for String literals- These values are hard-coded into the binary of the program, and therefore the size is fixed, and can be stored on the stack
- These behave like other “copy”-based types, like
i32,u32, etc…
fn main() {
// The following requires the use of .clone() in order to maintain x as a referenceable
// variable when assigning to y, because the String data type is present in the tuple
let x: (i32, i32, (), String) = (1, 2, (), String::from("hello"));
let y: (i32, i32, (), String) = x.clone();
// An alternative option that uses a string literal instead of a dynamic String
// This means that a simple assignment of b from a can be used as all types are
// copied
let a: (i32, i32, (), &str) = (1, 2, (), "hello");
let b: (i32, i32, (), &str) = a;
}
Mutability and Ownership
When changing memory ownership, the mutability of the original value can be changed too.
let x: String = String::from("hello, ");
// x.push_str("world!"); <- Results in an error!
let mut y: String = x;
y.push_str("world!"); // <- No error as we've made the new owner reference mutable
Boxed Types
You can use the Box type to explicitly force the storage of a value into heap memory when it might usually be stored in the stack.
See an example of this below the next section.
The * Operator
When using variables that contain pointers to heap-stored data, you can access the underlying value stored on the heap using the * operator. If you don’t use this, then the actual value returned when checking the variable would be a literal memory pointer address.
This process is called “dereferencing.”
fn main() {
// Stores 5 onto the heap when usually it'd be stack-based
let x: Box<i32> = Box::new(5);
let mut y: Box<i32> = Box::new(1);
// The * is needed here to "dereference" the value stored in memory, or to go to the
// heap memory location and access the actual data
*y = 4;
assert_eq!(*x, 5);
}
Open Question: I don’t know if this is always needed or simply when working with explicitly “boxed” variables? Like, do I need to do this when working with Strings and comparing strings using assert_eq!();?
I left off at starting to discuss Partial Move.