Reputation: 115
The phrase "when scope exits the values get automatically popped from stack" is repeated many times, but the example I provide here disproves the statement:
fn main() {
let foo = foobar();
println!("The address in main {:p}", &foo);
}
fn foobar() -> Employee {
let emp = Employee {
company: String::from("xyz"),
name: String::from("somename"),
age: 50,
};
println!("The address inside func {:p}", &emp);
emp
}
#[derive(Debug)]
struct Employee {
name: String,
company: String,
age: u32,
}
The output is:
The address inside func 0x7fffc34011e8
The address in main 0x7fffc34011e8
This makes sense. When I use Box
to create the struct the address differs as I expected.
I'm sure that Employee
doesn't implement the Copy
trait.
Upvotes: 2
Views: 913
Reputation: 16900
In many languages, variables are just a convenient means for
humans to name some values.
Even if on a logical point of view we can assume that there is one
specific storage for each specific variable, and we can reason about
this in terms of copy, move... it does not imply that these copies
and moves physically happen (and notably because of the optimizer).
Moreover, when reading various documents about Rust, we often find
the term binding instead of variable; this reinforces the idea
that we just refer to a value that exists somewhere.
It is exactly the same as writing let a=something();
then let b=a;
,
the again let c=b;
... we simply change our mind about the name
but no data is actually moved.
When it comes to debugging, the generated code is generally sub-optimal by giving each variable its own storage in order to inspect these variables in memory. This can be misleading about the true nature of the optimised code.
Back to your example, you detected that Rust decided to perform a kind of return-value-optimization (common C++ term nowadays) because it knows that a temporary value must appear in the calling context to provide the result, and this result comes from a local variable inside the function. So, instead of creating two different storages and copying or moving from one to another, it is better to use the same storage: the local variable is stored outside the function (where the result is expected). On the logical point of view it does not change anything but it is much more efficient. And when code inlining comes into play, no one can predict where our variables/values/bindings are actually stored.
Some comments below state that this return-value-optimisation can be counted on since it takes place in the Rust ABI. (I was not aware of that, still a beginner ;^)
Upvotes: 2