Move against copy in rust

Say I have this structure

struct Triplet { one: i32, two: i32, three: i32, } 

If you pass this to a function, it is implicitly copied. Now sometimes I read that some values ​​are not copied and therefore are forced to move. I wonder if it is possible to make this struct Triplet not copied in some way? Similar to the introduction of some additional feature that tells rust that it will not be copied and therefore should move instead.

I read everywhere that you need to implement the Clone trait to copy things that are implicitly copied, but I never read about what happens to what implicitly copied and makes it not copied, so instead it moves.

Does that even make sense?

+84
rust
Jun 16 '14 at 22:47
source share
2 answers

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.

+155
Jun 16 '14 at 23:14
source share

The easiest way is to paste something into your type that cannot be copied.

The standard library provides a “marker type” for just this use case: NoCopy . For example:

 struct Triplet { one: i32, two: i32, three: i32, nocopy: NoCopy, } 
+7
Jun 16 '14 at 23:10
source share



All Articles