Contrary to C/C++, Rust has very explicit distinction between types which are copied and which are moved. Note that this is only a semantic distinction; on the implementation level move is a shallow bytewise copy, however, the compiler places certain restrictions on what you can do with variables you moved from.
By default every type is only moveable (non-copyable). It means that values of such types are moved around:
let x = SomeNonCopyableType::new();
let y = x;
x.do_something(); // error!
do_something_else(x); // error!
You see, the value which was stored in x
has been moved to y
, and so you can't do anything with x
.
Move semantics is a very important part of ownership concept in Rust. You can read more on it in the official guide.
Some types, however, are simple enough so their bytewise copy is also their semantic copy: if you copy a value byte-by-byte, you will get a new completely independent value. For example, primitive numbers are such types. Such property is designated by Copy
trait in Rust, i.e. if a type implements Copy
, then values of this type are implicitly copyable. Copy
does not contain methods; it exists solely to mark that implementing types have certain property and so it is usually called a marker trait (as well as few other traits which do similar things).
However, it does not work for all types. For example, structures like dynamically allocated vectors cannot be automatically copyable: if they were, the address of the allocation contained in them would be byte-copied too, and then the destructor of such vector will be run twice over the same allocation, causing this pointer to be freed twice, which is a memory error.
So by default custom types in Rust are not copyable. But you can opt-in for it using #[derive(Copy, Clone)]
(or, as you noticed, using direct impl
; they are equivalent, but derive
usually reads better):
#[derive(Copy, Clone)]
struct MyType {
member: u16
}
(deriving Clone
is necessary because Copy
inherits Clone
, so everything which is Copy
must also be Clone
)
If your type can be automatically copyable in principle, that is, it doesn't have an associated destructor and all of its members are Copy
, then with derive
your type will also be Copy
.
You can use Copy
types in array initializer precisely because the array will be initialized with bytewise copies of the value used in this initializer, so your type has to implement Copy
to designate that it indeed can be automatically copied.
The above was the answer to 1 and 2. As for 3, yes, you are absolutely correct. It does work precisely because the value is moved into the function. If you tried to use a variable of MyType
type after you passed it into the function, you would quickly notice an error about using a moved value.