References and Borrowing

when i am reading the part about References and Borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#references-and-borrowing),i have a question。

fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

the picture
official

fn calculate_length(s: &String) -> usize { // s is a reference to a String
    s.len()
} // Here, s goes out of scope. But because it does not have ownership of what
  // it refers to, nothing happens.

as you see. the picture shows the relations between s ,s1 and s1's value in heap.
i can see the s's value is the ptr which refer to s1.although,it is not s1's owner.but why it's memory space won't be taking while the function calculate_length is over?

// Here, s goes out of scope. But because it does not have ownership of what*
// it refers to, nothing happens.

my questions is

why official document explain that nothing will happened to s?
Just because it does not have ownership of what it refers to?
but in the official picture, we can see the s have a ptr,right?
or just saying s'ptr is calculated,so it won't be store in anywhere so won't take any memory space?
If you know,could you explain it to me?thank you very much!

Hi,

i'm not totally sure whether i understood your question correctly, since English seems to be a foreign language for both of us, but let me try.

First of all, you seem to have changed the example from the book. Originally, calculate_length takes a reference (&String), while your version takes a value (String). In your example, the value may be copied three times (when creating s1, when calling calculate_length, and when returning). Since a string value is of variable length, it will never be allocated on the stack, but always on the heap. However, ptr, len and capacity are on the stack. If i'm not mistaken, the compiler may optimize some of the copies away.

1 Like

Hi @Xtreme-Light!

So, I'm not exactly sure I understand your question, but I'll do my best to explain and somebody please correct me if I'm wrong. :slight_smile:

So s has the type &String, which means it's a "reference to a string". A reference is a pointer that refers to another value. That pointer is stored on the stack and is pointing to s1 which is also on the stack. The actual string data in s1, because it needs to be able to be a string of any length is stored on the heap.

thank you first.because of some mistake,I gave something wrong.sorry。I fix it already.
about your reply.
I don't understand Since a string value is of variable length, it will never be allocated on the stack, but always on the heap.
the offical book say it is the special way for rust to manager it's memory, so why data in heap won't be freed?
on the other hand,the compiler may optimize some of the copies away. that's my question.
the s in picture have a ptr referring ti s1,will it store in stack?(because it iscalculate_length's paramter)would it be cleaned or freed?

thank u first.
but it don't solved my problem.
my question is from the offical book sayingHere, s goes out of scope. But because it does not have ownership of what
it refers to, nothing happens.
(the second code I showed) but I think s will take away.because the above picture showing and it is the function calculate_length's parameter.
weather s be freed?or not? and why?(In official Document,it says will do nothing)

It looks like your original example code block is still just a little off. It should look like this:

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

But, I think I understand you question now. You are wondering why the string data is still on the heap after calculate_length() is done running, right?

Let's go through this step-by-step:

fn main() {
    // First, we create the string. This is an "owned string". s1
    // "owns" the string data which is on the heap.
    let s1 = String::from("hello");

    // Now we we pass a "reference" to s1 into "calculate_length"
    // by prefixing s1 with an "&". Now lets go look at calculate_lenth
    let len = calculate_length(&s1);

    // So after our calculate length function is over, the reference
    // to s1 that we passed to it was dropped, but s1 is still in scope
    // here, so it still owns its data and the string data will
    // still exist on the heap.

    // To prove that we can still use s1, we print it. We can print s1
    // because it is still in scope and still owns its data
    println!("The length of '{}' is {}.", s1, len);

}  // Now that the main function over, our variable s1 goes out of scope
   // and is dropped. Because s1 owns its data, it will clean up its
   // data when it is dropped and delete the string data on the heap.



// OK, in this function we say that our argument, s, is a reference to a
// string. This means that it doesn't own the data that it refers to.
// Because s doesn't own its data someone else has to own it. In this case,
// s1 still owns its data.
fn calculate_length(s: &String) -> usize {
    // Here we use our reference to the string to read the string data and
    // count the length
    s.len()

}   // Now that our function is over, our local variable s will be dropped,
    // but because it doesn't own its data it doesn't delete the string.
    // That means that the string is still owned by s1 and still exists.

So, the reason that the string data isn't deleted when s goes out of scope is because s1, which owns the data, is still using it. So it can't delete the string data yet. Also, when you create a reference to something, it doesn't use up more heap memory. It just uses up a teeny-tiny bit of space on the stack, which is a pointer. So I could have a string that has a lot of references to it, but even if I had a lot of references, it wouldn't take up any space on the heap, it would just take up a little bit of space on the stack.

// Create a string
let my_string =
    String::from("Pretend this is a long string with lots of data");

do_something1(&my_string);
do_something2(&my_string);
do_something3(&my_string);
// And so on...

In the above example, every time I call one of the do_something functions, I pass a reference to my_string, but that reference is really tiny and doesn't take up a lot of memory. Each do_something function will be able to read the entire string from the heap, and even though we give a bunch of different references to my_string, my_string is only taking up one spot on the heap.

It will, once its owner goes out of scope.

In your example code, the String is implemented in a way that it owns its buffer, and it will indeed free the buffer behind its own pointer when it goes out of scope. Hence, you can think of a String (and owning types in general) as a pure, atomic value that you can move around, and it will take care of freeing any allocated memory (or other acquired resources) once it goes out of scope. The fact that it contains a pointer is an implementation detail at this point.

In contrast, a reference, e.g. &String does not own its pointed value. References are non-owning pointers, their only function is to provide indirection and a temporary view into another, owning value. You can't use a reference to keep its pointee alive, and similarly, you can't drop a reference hoping that it would also drop its pointee. References do not allocate and deallocate memory, and do not touch the heap (unless you point a reference to a value that is already on the heap itself).

If you want an owning pointer, which deallocates the pointee upon going out of scope, you can use Box instead. A Box is a smart pointer that allocates space on the heap, puts its pointee into that space, and deallocates the space (and calls the destructor of its pointee beforehand) when you're done with it. This is significantly different from the semantics of regular references.

1 Like

thank u very much!
your articl is very detailed。
after reading your reply,I think I understand what you say.
so,s the pointer,won't be freed or doing something beacause it is too tiny to take up a lot of memory.
so,in the official document,the comment,nothing happens. is totally right.
thank u very much again.:slight_smile:

1 Like

That's almost right. s the pointer will be deleted from the stack. It is tiny and it doesn't take up hardly any memory, but it will be deleted. What won't be deleted is the String data that is on the heap, because it isn't owned by the pointer.

I guess, that is somewhat unacurate actually, because the pointer is deleted, but it leaves the actual String data that holds "hello" in it alone.

Glad I could help! :smiley:

thank u!
References do not allocate and deallocate memory so the comment in official document ,"it will doing nothing" is right.so the references won't take any space and store in stack.
If the pointer will be allocated or deallocated, we could use Box instead.
by the way , I saw anther replying before yours in this topic said that pointer is too tiny to take up a lot of memory.so rust won't do anything.
It is different with References do not allocate and deallocate memory .
so ,it is hard for me to judge which one is right.
No offense, could u tell me what is the References do not allocate and deallocate memory come from?
so I can try to find some data to prove which one is right. That will solve my question throughly.

thank u again.

What @H2CO3 was talking about with Boxes is that you can use a Box to create a pointer to something that is on the heap. For instance, normally structs are stored on the stack. They don't allocate memory, so in the following example, there is no heap allocations:

struct MyStruct {
    my_number: i32
}

fn main() {
    // Create a new variable data1 on the stack
    let data1 = MyStruct {
        my_number: 5,
    };
}

But if for some reason you wanted to put a MyStruct on the heap, you could do so with a Box like this:

struct MyStruct {
    my_number: i32
}

fn main() {
    // Create a new variable data1 that is a pointer to MyStruct on the heap
    let data1 = Box::new(MyStruct {
        my_number: 5,
    });
}

// When data1 is dropped after main exits, because data1 is a special Box
// pointer, it will clean up MyStruct and delete it from the heap

thank @H2CO3 @zicklag !
With your help ,I come back to read official document again.
Only the data in heap need to be allocated and deallocated.
References just pushed and popped automatic.
So , you are right. My understand went wrong.
For a function, it's parameter or local variable will pushed and popped in stack automatic.
so for s, it don't need allocate or deallocate on purpose.
thank u again! :grin::grin::grin::grin::grin::grin::grin::grin::grin:

2 Likes

Exactly! :tada:

1 Like

They do not contradict. Both are right. I was referring to the fact that references don't own their pointed value.

Of course a reference itself still takes up some tiny amount of memory, which, however, can usually be allocated on the stack or stored in a register directly, since references are often short-lived and temporary.

There's no need for either of us to prove we are right – we weren't saying contradictory things.

1 Like

Yeah, I understood。 :nerd_face:
Thank u again.
I explained you are right in this replying already.
:grin: :grin: :grin:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.