Stack behavior when returning a pointer to local variable

740 Views Asked by At

I have a simple example where the behaviour of Rust does not match my mental image, so I am wondering what am I missing:

fn make_local_int_ptr() -> *const i32 {
    let a = 3;
    &a
}

fn main() {
    let my_ptr = make_local_int_ptr();
    println!("{}", unsafe { *my_ptr } );
}

Result:

3

This is not what I would expect. Using the notation given in The Stack and the Heap, I would expect the stack frame to look like this:

Address | Name | Value
-----------------------
   0    |   a  |   3  

inside make_local_int_ptr(), but after this line,

let my_ptr = make_local_int_ptr();

Since a goes out of scope, I would expect the stack to get cleared, but it apparently does not.

Furthermore, if I define another variable between creating my_ptr and printing the dereferenced value of it:

fn main() {
    let my_ptr = make_local_int_ptr();
    let b = 6;
    println!("{}", b); // We have to use b otherwise Rust
                       // compiler ignores it (I think)
    println!("{}", unsafe { *my_ptr } );
}

My output is:

6
0

Which again is not what I expected, I was thinking:

Address | Name | Value
-----------------------
   0    |   b  |   6

In which case my output would be:

6
6 

or even (in C++ and Go I was getting this result):

Address | Name | Value
-----------------------
   1    |   b  |   6  
   0    |   a  |   3

In which case my output would be:

6
3

Why am I getting the output that I am getting? Why is returning a pointer to a local variable even allowed? The variable goes out of scope, and the value where the pointer is pointing to becomes unpredictable.

1

There are 1 best solutions below

0
On BEST ANSWER

You shouldn't be returning a pointer to a local stack variable at all. Doing so is undefined behaviour, and the compiler is completely free to do whatever it wants.

When you say unsafe, you are promising the compiler that you will manually uphold all of its expected invariants... and then immediately breaking that promise.

To put it bluntly: you're violating memory safety, all bets are off. The solution is to not do that.


To explain why you might be seeing this behaviour, however (again, this is undefined behaviour, nothing is guaranteed): the stack isn't "cleared" in the sense that its overwritten with zeroes; it's just not valid to read from it any longer.

Also, because the call to make_local_int_ptr is finished, the compiler has no reason to preserve its stack space, so it can re-use the space for anything. The 0 is possibly due to the call to println!?