Reputation: 87
I'm currently working my way through the rustlings course and got confused when this exercise regarding move-semantics came up. Here is the gist of it:
fn main(){
let vec0 = Vec::new();
let mut vec1 = fill_vec(vec0);
//stuff happens
}
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
//do stuff
vec
}
From what I can tell, the immutable vec0
is being moved to fill_vec
as parameter vec
, which is then being mutably shadowed by let mut vec = vec;
. Since Vectors are references to heap-storage, does this imply that a clone()
takes place due to the shadowing, or is the same pointer reused and just made mutable? Why would shadowing be used in this case at all?
Upvotes: 3
Views: 210
Reputation: 42217
is the same pointer reused and just made mutable?
Yes. Well the same object entirely, at runtime it's essentially a no-op.
Why would shadowing be used in this case at all?
Some people like the pattern of temporally "locking" and "unlocking" bindings via shadowing e.g.
let a = …;
…
let mut a = a;
// mutate `a` in-place
let a = a; // lock to readonly again
That's really just personal preference. Here:
vec_mut
? Not like the source could be reused, it was moved from.mut vec: …
directly.That mostly comes down to personal choice.
Upvotes: 3
Reputation: 3057
Semantically, it is same object, it just changed a name in your case.
But in current implementation, it would copy your stack data to new place in stack so address of Vec object will change (but heap pointers remains the same). It is done this way, because you can hide old name with new object:
let v = make_first_vec();
let mut v = make_second_vec();
// old v still exists and it would be dropped only at end of block.
Better example (you can run it):
struct Droppable(i32);
impl Drop for Droppable{
fn drop(&mut self){
println!("Dropping {}", self.0);
}
}
fn main(){
// Same object moved
// Changes stack location but still same object
// dropped only once
println!("Same object moved begin");
{
let a = Droppable(1);
let old_ref = &a;
println!("Address is {}", &a as *const _ as usize);
let mut a = a;
println!("Address is {}", &a as *const _ as usize);
let a = a;
println!("Address is {}", &a as *const _ as usize);
// Cannot use old reference because old object is moved
// Compile error if uncommented
// println!("Old object is still alive and has {}", old_ref.0);
}
println!("Same object moved end");
// Different object hides
// dropped in reverse order
println!("Different object hides begin");
{
let a = Droppable(2);
let old_ref = &a;
println!("Address is {}", &a as *const _ as usize);
let a = Droppable(3);
println!("Address is {}", &a as *const _ as usize);
println!("Old object is still alive and has {}", old_ref.0);
}
println!("Different object hides end");
// Different object overrides
// old object dropped when overrided
println!("Different object override begin");
{
let mut a = Droppable(4);
let old_ref = &a;
println!("Address is {}", &a as *const _ as usize);
a = Droppable(5);
println!("Address is same {}", &a as *const _ as usize);
// Cannot use old reference because old object destroyed
// Compile error if uncommented
// println!("Old object is still alive and has {}", old_ref.0);
}
println!("Different object override end");
}
It prints this:
Same object moved begin
Address is 140736088967924
Address is 140736088967888
Address is 140736088967892
Dropping 1
Same object moved end
Different object hides begin
Address is 140736088967888
Address is 140736088967892
Old object is still alive and has 2
Dropping 3
Dropping 2
Different object hides end
Different object override begin
Address is 140736088967892
Dropping 4
Address is same 140736088967892
Dropping 5
Different object override end
Upvotes: 3