Variable created on the heap still looks on the stack

I am trying to create a variable on the stack and one on the heap:

fn main() {
   let mut x = 1;
   let mut y = &x;
   let mut z = Box::new(x);
   let mut w = Box::new(2);

   println!("&x = {:p}", &x);
   println!("&y = {:p}", &y);
   println!("&z = {:p}", &z);
   println!("&w = {:p}", &w);
}

This is the output:

&x = 0x7fffe51ba364
&y = 0x7fffe51ba368
&z = 0x7fffe51ba370
&w = 0x7fffe51ba378

All these addresses are consecutive, while I would expect the last one to be farther away, since it should be allocated on the heap.

What am I doing wrong?

The contents of the box, i.e., the number 2 is on the heap but the box itself (the pointer to the heap, etc.) is still on the stack.

4 Likes

Should have I done something like:

println!("&w = {:p}", &*w);

Or just:

println!("&z = {:p}", z);

The p formatter uses the Pointer trait, which is implemented for Box<T>.

Another think that is worth pointing out, is that Rust is allowed to optimise-out heap allocation and put value on stack. So you can never rely on the fact that heap allocation was performed. This is explicitly stated in the documentation of std::alloc::GlobalAlloc.

You must not rely on allocations actually happening, even if there are explicit heap allocations in the source. The optimizer may detect unused allocations that it can either eliminate entirely or move to the stack and thus never invoke the allocator. The optimizer may further assume that allocation is infallible, so code that used to fail due to allocator failures may now suddenly work because the optimizer worked around the need for an allocation.

3 Likes

If you didn't observe the addresses by printing them, the compiler may have put some of those variables into registers or omitted them from your program altogether.

Examining pointer addresses is not an effective way to estimate what the compiler does, because it changes what the optimizer is allowed to do in significant ways.

5 Likes