Memory management basics of Rust lang

i need some help with understanding of rust basics and memory basics at all. I think this will be helpful for those who come across this after me.

memory adress

(1) Here i create a function thats return pointer to a vector. Why there is two different adresses if there must be the same pointer?

fn get_pointer(vec: &[i32]) -> &[i32] {
    vec
}
fn main() {
    let vector = vec![1, 2, 3, 4, 5];

    let first_element = get_pointer(&vector);
    let vector: Vec<String> = Vec::new();
    println!(
        "Vector: {:p}, and func result: {:p}",
        &vector, first_element
    );
}

Output: Vector: 0x5181aff7e8, and func result: 0x17e5bad99b0

different adresses of one element?

(2) Now im creating a function thats return a pointer to the first element of a Vector

fn get_pointer_to_first_element(vec: &[i32]) -> &i32 {
    &vec[0];
} 

fn main() {
    let vector = vec![1, 2, 3, 4, 5];

    let first_element = get_pointer_to_first_element(&vector);

    println!(
        "from variable: {:p}, from function: {:p}",
        &vector[0], &first_element
    );
}

from variable: 0x1f9a887da80, from function: 0x15fe2ffb10

variables defined in functions

(3) Okay, i understand thats question is even more stupid, but i realy cant get it.

Here i create a pointer variable inside a function, and it must be deleted with its "value" when program leaves scope of a function, so why i can use that value after call of the function?

also bonus question about deleting value after shadowing, is that true? Why?

fn get_pointer_to_first_element(vec: &[i32]) -> &i32 {
    let pointer = &vec[0];
    pointer
} // here variable "pointer" and its value must be deleted, isn't it? how then i can operate it after calling a function?

fn main() {
    let vector = vec![1, 2, 3, 4, 5];

    let first_element = get_pointer_to_first_element(&vector);

    let vector: Vec<String> = Vec::new(); // Bonus question: "vector" variable is new now, so previous must be deleted?

    println!("first element is available and its {first_element}. ");
}

Vector: 0x5181aff7e8, and func result: 0x17e5bad99b0

I tried to use the same things with arrays, but result is the same.

>Solution :

In order:

  1. You allocated a new vector, so of course it’ll have a different address. first_element points to the original vector. It was initialized with that value and it’ll keep it. Even if you had not, your function takes a slice as an argument; not a Vec. Those are different types. The Vec is itself a pointer. The slice is the data on the heap it points to.
fn main() {
    let vector = vec![1, 2, 3];

    println!(
        "Normal: {:p}. Calculated /w a function: {:p}. Slice: {:p}.",
        &vector,
        get_vec_address(&vector),
        get_slice_address(&vector)
    );

    // This outputs:
    // Normal: 0x7fff91db1550. Calculated /w a function: 0x7fff91db1550. Slice: 0x55ddbc54a9d0.
}

fn get_vec_address(vec: &Vec<i32>) -> &Vec<i32> {
    vec
}

fn get_slice_address(slice: &[i32]) -> &[i32] {
    slice
}

Run this snippet on Rust Playground.

See this cheat sheet on containers for a visual understanding.

  1. As the comment says, you’re taking the reference of a reference.
  2. pointer isn’t deleted. It’s moved as the return value. To get a better understanding of scope, you might want to review the chapters on scope and lifetimes.

As for your bonus question, shadowing a variable doesn’t drop it. It’ll still live to the end of the scope. If there are existing references to it, it can still be used. For example, this is valid code:

fn main() {
    let vector = vec![1, 2, 3];
    let first_element = &vector[0];
    let vector = vec![4, 5, 6];
    
    println!("{first_element}");
    println!("{vector:?}");

    // This will print:
    // 1
    // [4, 5, 6]
}

Run this snippet on Rust Playground.

Leave a Reply