When you assign a value to another variable or pass it to a function, Rust moves the value rather than copying it by default. After a move, the source binding is invalidated, and any attempt to use it is rejected at compile time. This design avoids double-free bugs that would otherwise arise if two bindings each believed they owned the same heap allocation: copying the pointer of a String would produce two would-be droppers of the same buffer. Move semantics keep the type system simple by ensuring only one entity is ever responsible for cleanup.
Some types opt out of moving through the Copy trait, a marker trait that signifies bitwise duplication is safe and that no special cleanup is needed. Integers, bool, char, tuples whose elements are all Copy, and any other type without heap-allocated resources typically implement Copy. The compiler refuses to let a type implement both Copy and Drop, because Copy implies that nothing of importance happens at the end of the value's lifetime. Copy technically requires Clone, but the two traits have very different flavors: Copy is implicit and free, while Clone is an explicit method .clone() that may perform arbitrary and potentially expensive work to produce a deep, independent copy.
Calling clone() is the standard way to deliberately duplicate data so that two bindings each own their own copy. It should be used when an independent value is genuinely required — for example, when multiple threads each need their own data — rather than as a habitual escape hatch from the borrow checker. The Clone trait has a simple signature, fn clone(&self) -> Self, taking a shared reference and returning a freshly owned value. Arrays follow the same rules as their elements: an array of Copy integers is itself Copy and duplicates on assignment, while an array of String is not Copy and will move element-by-element when needed.