How to force the movement of a type that implements the Copy property?

The default user type moves to the default destination. By implementing the Copy attribute, I get a “shallow copy semantics” by default. I can also get "deep copy semantics" by implementing the Clone attribute.

Is there a way to force movement of type Copy ?

I tried using the move keyword and closure ( let new_id = move || id; ), but I get an error. I have not closed yet, but, having seen them here and there, I thought that it would work.

+5
source share
4 answers

I do not understand your question, but of course you are embarrassed. Therefore, I will turn to what seems to be the root of this confusion:

C ++ concepts about copying / moving I think I understood correctly, but it is “still memcpy anyway”, well, it wasn’t very intuitive when I read it

When we think about the semantics of moving Rust, ignore C ++ . The history of C ++ is much more complicated than Rust's, which is surprisingly simple. However, explaining Rust semantics in C ++ terms is a mess.

TL DR: Copies move. Movements are copies. The difference is only in the type controller. Therefore, when you want to “force movement” for type Copy , you ask for something that you already have.

So, we have three semantics:

  • let a = b where b not Copy
  • let a = b where b Copy
  • let a = b.clone() , where b is Clone

Note. There is no significant difference between assignment and initialization (for example, in C ++) - assigning only the first drop old value.

Note. Function call arguments work just like assignment. f(b) assigns b f .


Primarily.

a = b always executes memcpy .

This is true in all three cases .

  • When you do let a = b , b is memcpy 'd in a .
  • When you do let a = b.clone() , the result of b.clone() is memcpy 'd in a .

Moves

Imagine b was Vec . A Vec as follows:

 { &mut data, length, capacity } 

When you write let a = b , you end up with:

 b = { &mut data, length, capacity } a = { &mut data, length, capacity } 

This means that a and b both &mut data links, which means we have alias mutable data.

The type system is not like it, so it says that we cannot use b again. Any access to b will fail at compile time.

Note. a and b do not need to have a heap data alias to use as a bad idea. For example, both of them can be file descriptors - a copy will cause the file to be closed twice.

Note. Moves have extra semantics when destructors are involved, but the compiler will not let you write Copy for types with destructors.


Copy

Imagine b was Option<i32> . Option<i32> looks like this:

 { is_valid, data } 

When you write let a = b , you end up with:

 b = { is_valid, data } a = { is_valid, data } 

They are both used simultaneously. To tell the type system that it is, mark Option<i32> as Copy .

Note. Marking something with a copy does not change what the code does. This allows more code. If you remove the Copy implementation, your code will either be a mistake or it will do the same. In the same vein, marking Copy as Copy will not change the compiled code.


Clones

Imagine you want to copy Vec . You implement Clone , which creates a new Vec and makes

 let a = b.clone() 

Two steps are performed. Let's start with:

 b = { &mut data, length, capacity } 

Running b.clone() gives us an extra temporary rvalue value

 b = { &mut data, length, capacity } { &mut copy, length, capacity } // temporary 

Running let a = b.clone() memcpy in a :

 b = { &mut data, length, capacity } { &mut copy, length, capacity } // temporary a = { &mut copy, length, capacity } 

Further access to the temporary is thus prevented by the type system, since Vec not Copy .


But what about efficiency?

One thing that I skipped so far is that transfers and copies can be undone. Rust ensures that some trivial moves and copies are undone.

Since the compiler (after checking the life cycle) sees the same result in both cases, they are eliminated in exactly the same way.

+15
source

Wrap the copy type with another type that does not implement Copy .

 struct Noncopyable<T>(T); fn main() { let v0 = Noncopyable(1); let v1 = v0; println!("{}", v0.0); // error: use of moved value: `v0.0` } 
+6
source

New answer

Sometimes I just want him to shout at me "introduced a new meaning here!".

Then the answer is no. When moving a type that implements Copy , both the source and destination will always be valid. When moving a type that Copy does not implement, the source will never be valid, and the destination will always be valid. There is no syntax or attribute that means "let me choose if this type that implements Copy acts like Copy at this time."

Original answer

I just want to say yes, this type is Copy, but I no longer need this value in this variable. This function takes a val argument, just take it. "

It looks like you are trying to make the optimizer work manually. Do not worry about it, the optimizer will do it for you. This may not worry about it.

+4
source

At run time, copies and moves in Rust have the same effect. However, during compilation, in the case of moving, the variable from which the object is being moved is marked as unusable, but not in the case of a copy.

When you use Copy types, you always want the semantics of a value and the semantics of an object if you do not use Copy types.

Objects in Rust do not have a consistent address: addresses often change between moves due to runtime behavior, i.e. they belong to only one binding. This is very different from other languages!

0
source

All Articles