Even in Rust, the `Clone` trait produces a deep copy.
Copying only up to a specific depth in a general way is what is difficult.
I would argue that shallow copies, id est the depth being 1, are far more difficult to realize that copies of unbounded depth until a value o trait `Copy` is reached, id est a type that is purely encoded by the data it takes up on the stack and owns no further data and can thus be fully be `Clone`ed by copying it's stack bits.
> Even in Rust, the `Clone` trait produces a deep copy.
Clone is not really a deep copy. I like the description that says it is "deep enough to give you an object of identical/independent ownership (of what you started with), but no deeper".
Example: Rc<String> when cloned only gives you another handle to the Rc, the string data is not duplicated (for this reason we don't call it a real deep copy). You get a new rc handle that is on equal footing with the old handle.
There is plenty of Rust types that consist of tree-shaped ownership relations with no frills - in these cases clone and deep copy are identical. Let's say for example a HashMap<i32, String>.
I think every "deep" copy ultimately has some limitation like this. Even in Python, an object can implement __deepcopy__() to prevent copy.deepcopy() from "copying too much", and things like file objects, classes, and modules get special treatment.
I found that in Rust, you don't have to think about the distinction so much - not like you have to in for example Python and C# (pervasive/transparent object reference languages). In Rust it's predictable and mostly obvious which data is shared ownership or shared, and which is not. We don't need to talk about deep vs shallow copy semantics much in Rust. :)
It's not intrinsically difficult, but in Python it's arguably un-Pythonic and somewhat awkward. Like, sharing references has built-in syntax that's taught to most Python programmers within their first hour with the language, whereas copying - without writing a tedious manual implementation - requires importing the `copy` module [0], which outlines a number of pitfalls, quirks, and caveats with the process, and that I've literally never used so far as I can recall in a decade of professional Python development.
You have to pass around the whole object you want to copy for that to work. For example in a compiler, if you function only sees an AstNode, it can't make a deep copy of the whole AST easily. You also have to be careful with things like objects that contain refcounted pointers.
Even in Rust, the `Clone` trait produces a deep copy.
Copying only up to a specific depth in a general way is what is difficult.
I would argue that shallow copies, id est the depth being 1, are far more difficult to realize that copies of unbounded depth until a value o trait `Copy` is reached, id est a type that is purely encoded by the data it takes up on the stack and owns no further data and can thus be fully be `Clone`ed by copying it's stack bits.