<< Previous | Next >>

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.

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.

References