About picture in ch4.2 references and borrowing

Hi community,

I'm bit confuse on the picture.

If s.ptr points to s1, shouldn't it need one more indirection
and the expression be like this:

(*s).len()

in the function:

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

Shouldn't s.ptr point to the heap data, like s1.ptr too?
s references to the data but don't own the data.
In my understanding, reference in Rust is like reference in C++
without the pointer syntax. Is it correct?

Thanks


https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html

Figure 4-5 in Chapter 4.2


Is the following new picture correct?

len(&self) is a method of the String type. The &self parameter means that it needs a reference to the stack-allocated¹ String to operate, which in turn contains a pointer onto the heap where the characters are stored.

In this case, s contains a pointer to s1 within main's stack frame, and s1 contains a pointer onto the heap. Inside String::len(), self also contains a pointer into main's stack frame (referring to s1), and it derefetences that pointer to read the len field (which is completely different from the len() method, despite sharing a name).

¹ The method doesn't really care which part of memory the String is stored in, but it's on the stack in this example.

2 Likes

It is not. s has type reference-to-String, so it points to a string, which in turn points to a heap buffer. The image in the book is correct.

What might have confused you is the "ptr" in s. There are of course no fields in references – references aren't structs, they are pointers, so the ptr is not a field of the type &String itself; the pointer is the address of the value where the reference points (which happens to be th address of s1 in the example).

4 Likes

Method resolution and field access can go through multiple layers of autoderef.

1 Like

Thanks,

Rust simplified the syntax and made the following two calls the same calling method on object or reference, right?

    let s = String::from("hello");

    let len1 = s.len();
    let len2 = (&s).len(); // let len2 = &s.len();

The s.len() syntax is just syntactic sugar for calling String::len(&s).

Your &s.len() isn't quite right because of precedence - you are calling s.len() and then taking a reference to the result (i.e. len2 is a &usize). I'm guessing you meant let len2 = (&s).len()?

2 Likes

Thanks,

You're right. Are the two calls the same?

s.len()
(&s).len()

I would say that they are "equivalent" and will give you the same result (by calling String::len() with a reference to a String).

The compiler may or may not generate some extra machine instructions in the second one because you are taking a reference - it really depends on the Rust compiler and LLVM's optimiser/code generator.

2 Likes

I’ll also add that it’s possible for a user-defined type to be written so that these two forms aren’t equivalent. It takes a bit of doing, though, and is usually a bad idea.

3 Likes

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.