Hands-On Data Structures and Algorithms with Rust
上QQ阅读APP看书,第一时间看更新

Copying and cloning

In Chapter 1, Hello Rust!, we discussed Send, a marker trait that allows a type to be "sent" across multiple threads. Something that's similar but less complex is local moving, which commonly occurs in a program—for example, when you pass a variable into a function.

Copying and cloning, on the other hand, happen on different occasions. When a variable is assigned to another variable, the compiler will typically copy the value implicitly, which can be done safely and cheaply for stack-allocated variables.

Copy is an implicit, bitwise copy of the value of a variable. If that variable is a pointer, the memory responsibility becomes ambiguous (who takes care of freeing?) and compilation will fail. This is where Clone comes in. The trait requires an explicit implementation of the clone() function to provide an appropriate copy of the type.

Cloning is always a deep copy of a type—implemented either manually (with the Clone trait) or by using the derive macro. Then, cloning is only a matter of invoking the clone() function, an operation that is not necessarily cheap. The following snippet illustrates these two operations:

let y = 5;
let x = y; // Copy

let a = Rc::new(5);
let b = a.clone(); // Clone

The regular usage of these traits and operations is usually intuitive and there isn't much that can go wrong. Usually the compiler clearly states the need for a Copy implementation.

It's recommended to derive or implement Copy wherever possible, but be mindful of breaking changes. Adding the trait is a non-intrusive action, whereas removing the trait will potentially break other people's code.

While copying and cloning are great for providing ownership to multiple scopes, they are required when working with immutable storage.