Let’s see… the most important piece of information to keep in mind here, and something where Rust differs from C++, is that neither Copy
nor moves are in any way customizable with user code. They always do exactly that single memcpy
of size_of::<T>()
many bytes, the shallow size of the struct
(or enum
, or array) in question. For large array types (array means, the type “[T; N]
”) or structs containing such types, or extreme cases of other deeply nested structs, this can sometimes be nontrivial overhead (and those kind of types also tend to be able to eventually overflow your stack anyways, when you work with them). But in most cases, std::mem::size_of::<Type>()
will be rather small for most types, so it’s cheap to copy or move.
As a consequence, it’s also never more expensive to copy instead of to move.
For Vec<Bar>
, that type is a (growable) vector on the heap. The size_of::<Vec<Bar>>()
on the other hand is always just 3 * size_of::<usize>()
. So that’s how much a move costs. What about a copy? Vec<T>
is a type that owns memory, and needs a custom destructor. Those types are prevented from implementing Copy
in the first place. So implicit copying of a Vec
is impossible in Rust. (Yay, no accidental implicit and unwanted deep or semi-deep copies!)
If you want to copy a Vec
, you’d use the Clone
trait instead, through which many types gain a .clone()
method. That’s expensive then for a large Vec<Bar>
, but to do it to foo: Vec<Bar>
, you’ll have to write foo.clone()
explicitly.
So, no, Rust has nothing like std::move
in C++, but that’s because it doesn’t need it. A move and a (implicit) copy in Rust are each built-in operations that are (generally) cheap, and not user-definable; and for a type that supports (implicit) copying, a move would never actually be cheaper; their run-time behavior is essentially identical.
The only setting in which you do effectively really move a value of some Copy
-able type anyways is in generic settings. E.g. you write a function
fn foo<T>(x: T) {
let y = x; // moves `x`, even in cases when `foo` is called with type `T` whose values copyable
}
this does do a move, even when called as foo(3)
so with T = i32
.