Reference to a Reference

Hi,

I would like to know what happens if I take reference to reference. I do that in the following program. and works well !!!.

What I want to know is that if I take reference to a reference, whether, compiler creates a new reference pointing to an existing reference or will Rust understands that reference to a reference is still same and hence there needs to be no chaining and all references to to the original value 10. The hand diagram illustrates the question.

fn main()
{

    let x= 10;

    let y = &x ;
    /****
    /&x is reference to 10
    and y points to that reference. So
    y is reference.
    ****/

    let z= &y;

    let z1 = &z;

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

Thanks,
S.Gopinath

1 Like

There are two tools I know of that are particularly useful for answering these kinds of questions, of the form "how does the compiler generate code for a given input?"

Pasting your example into the compiler explorer provides the following output: https://rust.godbolt.org/z/VAt-7r

I have annotated the assembly below with the Rust code in comments:

example::main:
    sub     rsp, 104

    // let x= 10;
    mov     dword ptr [rsp + 12], 10
    lea     rax, [rsp + 12]

    // let y = &x ;
    mov     qword ptr [rsp + 16], rax
    lea     rax, [rsp + 16]

    // let z= &y;
    mov     qword ptr [rsp + 24], rax
    lea     rax, [rsp + 24]

    // let z1 = &z;
    mov     qword ptr [rsp + 32], rax
    lea     rax, [rsp + 32]

    // println!("{}", z1);
    mov     qword ptr [rsp + 40], rax
    lea     rax, [rip + <&T as core::fmt::Display>::fmt]
    mov     qword ptr [rsp + 48], rax
    lea     rax, [rip + .L__unnamed_1]
    mov     qword ptr [rsp + 56], rax
    mov     qword ptr [rsp + 64], 2
    mov     qword ptr [rsp + 72], 0
    lea     rax, [rsp + 40]
    mov     qword ptr [rsp + 88], rax
    mov     qword ptr [rsp + 96], 1
    lea     rdi, [rsp + 56]
    call    qword ptr [rip + std::io::stdio::_print@GOTPCREL]

    add     rsp, 104
    ret

As you can see, all variables (x, y, z, z1) are given their own stack locations. And furthermore each variable (except x for obvious reasons) is a pointer to the last. This is the code as you have written it (e.g. it represents the chain of references in your second diagram). The compiler also outputs a formatter which walks the chain:

<&T as core::fmt::Display>::fmt:
    mov     rax, qword ptr [rdi]
    mov     rax, qword ptr [rax]
    mov     rdi, qword ptr [rax]
    jmp     qword ptr [rip + _ZN4core3fmt3num3imp52_$LT$impl$u20$core..fmt..Display$u20$for$u20$i32$GT$3fmt17hbb27e86dfb8ce1d4E@GOTPCREL]

I don't know if this is actually an issue in practice, since Rust employs Deref coercions in the type system. E.g. when passing &&T to a function or value which is statically guaranteed to be &T, you get &T. You can see in this case, the stack slots for each variable get optimized out. The code is not storing pointers-to-pointers, just a single pointer to a u32.

If you really truly need to write this kind of reference-to-reference code, you could avoid type inference by annotating the types on each of your variables, or at the very least only on the last one in the chain: https://rust.godbolt.org/z/js2q3C

3 Likes

Thanks for your kind reply. More I do program on Rust, more clarifications I sought..

I suppose, the same logic applies to other type which dont implement Copy trait.

Thanks,
S.Gopinath

You will have a pointer to a pointer (i.e. multiple layers of indirection).

It's a priority in the language to prefer explicitness over implicit magic, and having the compiler implicitly peel away the references.

This sort compiler transformation could also change the meaning of your program. For example, maybe you wanted the second level of indirection so you can make it point to something else later on.

Thanks...

Like this ..... this makes more deterministic and clean!
"It's a priority in the language to prefer explicitness over implicit magic, and having the compiler implicitly peel away the references"

1 Like

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.