Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do values in return position always get allocated in the parents stack frame or receiving Box?

Tags:

rust

I was trying to understand how structs behave when returned from methods. There is a section in the nightlies section of the "Rust Book" that said if you used the syntax...

let x = box i_return_a_struct();

.. that there wouldn't be a copy, therefore there is no need to return a pointer. But when I started playing with it, it appears that the box is not needed, unless you need the value to exist on the heap.

#[derive(Debug)]
struct Dummy {
    data: i64,
}

impl Drop for Dummy {
    fn drop(&mut self) {
        println!("{:?} is going out of scope", self as *const Dummy);
    }
}

fn make_dummy(i: i64) -> Dummy {
    Dummy { data: i }
}

fn main() {
    {
        let i = 15i32;
        println!("{:?} is a variable on the stack frame", &i as *const i32);

        let dummy1 = make_dummy(1);
        println!("{:?} was returned and is being used", &dummy1 as *const Dummy);
        let dummy2 = make_dummy(2);
        println!("{:?} was returned and is being used", &dummy2 as *const Dummy);

        let dummy3 = Box::new(make_dummy(3));
        println!("{:?} box was returned and is being used", &(*dummy3) as *const Dummy);
        let dummy4 = Box::new(make_dummy(4));
        println!("{:?} box was returned and is being used", &(*dummy4) as *const Dummy);
    }
    println!("Leaving main");
}

Output:

0x23fb94 is a variable on the stack frame
0x23faf8 was returned and is being used
0x23fa50 was returned and is being used
0x2825030 box was returned and is being used
0x2825040 box was returned and is being used
0x2825040 is going out of scope
0x2825030 is going out of scope
0x23fa50 is going out of scope
0x23faf8 is going out of scope
Leaving main

Do values/structs in return position always get allocated in the parents stack frame or receiving box?

EDIT: PS - is there any guidance in the docs for as to when copy elision will occur in general?

EDIT: Beyond the accepted solution, the following Q+A was enlightening: What are move semantics exactly? Clarified many points for me.

like image 701
Rich Henry Avatar asked Jul 19 '15 15:07

Rich Henry


1 Answers

Maybe it's not clear to me what you don't understand. I think you understand, but maybe you don't know yet :D

Normally, the return value of a function (make_dummy for example) is pushed on the stack. Now suppose you want the object on the heap instead. With the new box syntax the compiler can do some optimization if you want the object on the heap.

Now let's take the example from the book.

let y: Box<Dummy> = box make_dummy(some_dummy);

You may think that in the above example the following happens:

  1. the return value from make_dummy is written into the stack (as normally)
  2. a box is allocated to contain a Dummy object
  3. the Dummy value on the stack is copied in the memory pointer by the box object.

With the old Box::new mechanism this is exactly what would happen. Instead, thanks to the experimental box syntax, this happened:

  1. A box is allocated
  2. The pointer to that box is passed in some way to the make_dummy function (with some compiler magic), so the return value is written directly to the boxed memory [there's no extra copies involving the stack]

I hope it's more clear now.

like image 104
eulerdisk Avatar answered Oct 17 '22 10:10

eulerdisk