I think it's worth understanding why COP is necessary instead of MOV.
MOV only works on atomic datatypes (foundational datatypes, not built off other datatypes), and even then not all of them. It works on DINT, REAL, INT, etc. MOV is a lot smarter than COP and actually tries to understand what the programmer is doing, so it, for example, can handle a MOV between a REAL and a DINT, because the MOV knows that the programmer wants the two values to match as closely as possible (this is still bad practice - if you ever make money off PLC programming please don't do this).
Strings are not atomic datatypes. They are comprised of a single DINT (or similar) indicating how many characters are in the string and an array of SINTS (8 bit integers or similar) representing each character in the string using ASCII encoding.
Where MOVs have the benefit of being smart, COPs have the benefit of being able to operate on any datatype. In a string's case, the datatype is the length integer and the array of character integers. A COP instruction can do this because the COP instruction is dumb. It just takes the binary values in the source memory register and pushes them to the destination memory register. It does not care what is actually assigned at either register.
To test it out, try doing a MOV between a DINT and a REAL and then try the same but with a COP. Both a DINT and a REAL are 32 bits but you will see very different behavior. This is because the MOV knows what you want, the COP does not. The way the PLC stores a DINT and a REAL, while both 32 bits, are very different behind the scenes, and results in very strange behavior if you aren't aware of what is going on.