A variable starts its life as a reference?

#1

I am sorry for my ignorance but while watching the ownership chapter of the Rust in Motion video I stuck upon this question. I haven’t seen lifetime yet and maybe this is the solution. But allow me to ask the following.

fn main() {
    let s: &String = &String::from("something");
}

or something more complex in order not to fall into any special case:

fn main() {
    let s: &mut String = &mut String::from("something");

    s.push_str(" more");

    println!("{}", s);
}

What happens when a variable starts its life as a (borrow) reference? Specifically, I would like
to ask two questions:

  1. Firstly, when s goes out of scope, the backing allocated memory is not going to be freed, because s is a borrowed reference. So, how and when the backing memory is dropped?

  2. Secondly, I am tring to have a visual model of what is happening in terms of memory layout. Namely, s is a reference variable (pointer) on the stack frame of main that points to a String value (i.e. the struct of the triplet of a pointer to the data, the length, and capacity). Is this String value on the heap in this case like the figure below?

#2

In this case, it seems that s is just &'static String, i.e. the compiler can prove that the String it is referencing can be valid for the whole program time. So, according to your questions:

  1. The memory is either dropped at the end of main, or not dropped at all, depending on whether the allocation really takes place. This leads us to the second question:

  2. It seems to depend on optimisations. I’ve tried to check this on playground. I can’t read the assembly very well, but as far as I can tell:

    • In Debug mode, there is an alloc call for the String contents, which returns the fat pointer and places it in the local variable, so that s references this local variable.
    • In Release mode, there is no allocation at all - string is just passed through from the data part of the binary into the output function.
1 Like
#3

So, as far as the visual model is concerned, the situation might be like this figure

or this

1 Like
#4

This is wrong.

The compiler does the equivalent of:

fn main() {
    let hidden = String::from("something");
    let s: &String = &hidden;
}

That is, if you take a borrow of a temporary, it stores that temporary in an invisible variable just “before” the enclosing statement.

It will be freed, because it’s owned by the invisible variable.

9 Likes
#5

Thanks, missed this point.