Preface: This answer was written before the built-in tags , especially Copy aspects were implemented. I used block quotes to indicate sections that apply only to the old scheme (the one that was applied when asking the question).
The old one . To answer the basic question, you can add a marker field that preserves the NoCopy value. For example.
struct Triplet { one: int, two: int, three: int, _marker: NoCopy }
You can also do this using a destructor (using Drop trait ), but using marker types is preferred if the destructor does nothing.
Now types are now moved by default, that is, when you define a new type, it does not implement Copy , unless you explicitly implement it for your type:
struct Triplet { one: i32, two: i32, three: i32 } impl Copy for Triplet {} // add this for copy, leave it out for move
An implementation can exist only if each type contained in the new struct or enum is Copy itself. If not, the compiler will print an error message. It can also exist only if the type does not have a Drop implementation.
To answer the question that you did not ask ... "what about the movements and the copy?":
First, I define two different copies:
- copy of a byte that just finely copies the object byte by byte, and not the following pointers, for example. if you have
(&usize, u64) , this is 16 bytes on a 64-bit computer, and a shallow copy will take those 16 bytes and replicate their value to the other 16-bit part of the memory, without touching usize to the other end & . That is, this is equivalent to calling memcpy . - a semantic copy that duplicates the meaning of creating a new (several) independent instance that can be safely used separately for the old. For example. the semantic copy of
Rc<T> includes only increasing the reference counter, and the semantic copy of Vec<T> involves creating a new selection, and then semantic copying of each saved item from the old to the new. It can be deep copies (for example, Vec<T> ) or shallow (for example, Rc<T> does not apply to the stored T ), Clone is defined as the minimum amount of work required to semantically copy a value of type T from inside a &T to T
Rust is similar to C, each use of value by value is a byte copy:
let x: T = ...; let y: T = x; // byte copy fn foo(z: T) -> T { return z // byte copy } foo(y) // byte copy
They are byte copies regardless of whether or not T moves or is "implicitly copied". (To be clear, they are not necessarily literally byte copies at runtime: the compiler can freely optimize copies if the behavior of the code is preserved.)
However, there is a fundamental problem with byte copies: you get duplicate values in memory, which can be very bad if they have destructors, for example.
{ let v: Vec<u8> = vec![1, 2, 3]; let w: Vec<u8> = v; } // destructors run here
If w was just a simple byte copy of v , then there would be two vectors pointing to the same distribution as with destructors freeing it ... calling a double free , which is a problem. NB. It would be wonderful if we made a semantic copy of v in w , since then w will be its own independent Vec<u8> , and destructors will not Vec<u8> from each other.
There are several possible fixes:
- Let the programmer deal with this, like C. (there are no destructors in C, so it's not that bad ... you just stay with memory leaks .: P)
- Performing a semantic copy is implicit, so
w has its own selection, for example C ++ with its copy constructors. - Regard by-value uses as a transfer of ownership, so
v can no longer be used and does not execute its destructor.
The last is what Rust does: relocation is just value-based use when the source is statically invalid, so the compiler prevents further use of invalid memory.
let v: Vec<u8> = vec![1, 2, 3]; let w: Vec<u8> = v; println!("{}", v); // error: use of moved value
Types that have destructors must be moved when used by value (for example, when copying a byte), since they have control / ownership of some resource (for example, memory allocation or a file descriptor), and it is very unlikely that the byte A copy will duplicate this ownership right.
"Well ... what an implicit copy?"
Think of a primitive type like u8 : a byte copy is simple, just copy one byte, and a semantic copy will be just as simple, copy one byte. In particular, a byte copy is a semantic copy ... Rust even has a built-in Copy attribute that captures which types have identical semantic and byte copies.
Therefore, for these Copy types, using by-value is also automatically semantic copies, and therefore it is completely safe to continue using the source.
let v: u8 = 1; let w: u8 = v; println!("{}", v); // perfectly fine
Old : The NoCopy marker overrides the compiler’s automatic behavior, assuming that the types that can be Copy (i.e., containing only aggregates of primitives and & ) are Copy . However, this will change when built-in functions are implemented.
As mentioned above, built-in built-in functions are implemented, so the compiler no longer has automatic behavior. However, the rule used for automatic behavior in the past is the same rules for checking whether Copy is legal.