Moving a value will do a trivial copy of the value's bytes from the old location into the new location. In the case of moving a Vec<T>
, it'll just copy the pointer (just the pointer, not the data behind it), length, and capacity fields into the new variable and treat the previous variable as uninitialized.
In a lot of cases, the optimizer may be able to skip moves that are unnecessary (e.g. let x = vec![...]; let y = x; let z = y;
could just be written let z = vec![...]
), but that isn't something you will ever really be able to observe.
You can also see this for yourself. I've copied your code into the playground, with the following main()
function:
fn main() {
let large = Large {
field1: 42,
large_vec: vec![0; 256],
};
print_len(&small);
}
#[inline(never)]
fn print_len(small: &Small) {
println!("{}", small.small_vec.len());
}
Compiling in release mode generates the following assembly for main()
:
playground::main:
pushq %rbx
subq $32, %rsp
movl $1024, %edi
movl $4, %esi
callq *__rust_alloc_zeroed@GOTPCREL(%rip)
testq %rax, %rax
je .LBB6_4
movq .L__unnamed_2(%rip), %rcx
xorps %xmm0, %xmm0
movups %xmm0, 8(%rsp)
movl $42, 24(%rsp)
movq %rcx, (%rsp)
movl $1024, %esi
movl $4, %edx
movq %rax, %rdi
callq *__rust_dealloc@GOTPCREL(%rip)
movq %rsp, %rdi
callq playground::print_len
addq $32, %rsp
popq %rbx
retq
.LBB6_4:
movl $1024, %edi
movl $4, %esi
callq *alloc::alloc::handle_alloc_error@GOTPCREL(%rip)
ud2
movq %rax, %rbx
movq %rsp, %rdi
callq core::ptr::drop_in_place<playground::Small>
movq %rbx, %rdi
callq _Unwind_Resume@PLT
ud2
It looks awfully complicated, but we are just shuffling some numbers around with no loops or expensive calls to memcpy()
. The je .LBB6_4
just checks whether allocating the buffer for our vec![]
failed and jumps to a alloc::alloc::handle_alloc_error()
call so we can handle it.