I really struggle to understand the PoV of the author in his The rules themselves are unergonomical section:
> But what's the point of the rules in this case, though? Here, the ownership rules does not prevent use after free, or double free, or data races, or any other bug. It's perfectly clear to a human that this code is fine and doesn't have any actual ownership issues
I mean, of course there is an obvious ownership issue with the code above, how are the destructors supposed to be ran without freeing the Id object twice?
The whole point is that `Id` doesn't have a destructor (it's purely stack-allocated); that is, conceptually it _could_ be `Copy`.
A more precise way to phrase what he's getting at would be something like "all types that _can_ implement `Copy` should do so automatically unless you opt out", which is not a crazy thing to want, but also not very important (the ergonomic effect of this papercut is pretty close to zero).
I think the primary reason Rust doesn't do this is because it's a semver hazard. I.e., adding a non-Copy field would silently break downstream dependents. Yeah, this is already a problem with the existing auto traits, but types that don't implement those are rarer than types that don't implement Copy.
> A more precise way to phrase what he's getting at would be something like "all types that _can_ implement `Copy` should do so automatically unless you opt out", which is not a crazy thing to want,
From a memory safety PoV it's indeed entirely valid, but from a programming logic standpoint it sounds like a net regression. Rust's move semantics are such a bliss compared to the hidden copies you have in Go (Go not having pointer semantics by default is one of my biggest gripe with the language).
I think you are misunderstanding what Copy means, and perhaps confusing it with Clone. A type being Copy has no effect on what machine code is emitted when you write "x = y". In either case, it is moved by copying the bit pattern.
The only thing that changes if the type is Copy is that after executing that line, you are still allowed to use y.
I'm not misunderstanding. Ore confusing the two. Copy: Clone.
Yes when an item is Copy-ed, you are still allowed to use it, but it means that you now have two independent copies of the same thing, and you may edit one, then use the other, and be surprised that it hasn't been updated. (When I briefly worked with Go, junior developers with mostly JavaScript or Python experience would fall into this trap all the time). And given that most languages nowadays have pointer semantics, having default copy types would lead to a very confusing situation: people would need to learn about value semantics AND about move semantics for objects with a destructor (including all collections).
No thanks. Rust is already complex enough for beginners to grasp.
Is it a particularly terrible thing, in and of itself, to pass structs by value? Less implicit aliasing seems less bug-prone. Note that Go only has implicit shallow copies (i.e., this only affects the direct fields of a struct or array); all other builtin types either are deeply immutable (booleans, numbers, strings), can point to other variables (pointers, slices, interfaces, functions), or are implicit references to something that can't be passed by value (maps, channels).
I'd argue that move semantics is strictly superior to value semantics.
But more importantly, my point is that in a world where pretty much all modern languages except Go have pointer semantics by default, and when you language needs to have move semantics for memory safety in many cases, having yet another alien (to most developers) behavior is really pushing the complexity bar up for no particular gains.
Actually; I'm not sure I'm wrong. If Copy was automatically derived based on fields of a struct (without the user explicitly asking for it with `#[derive(Copy)]` that is, as the parent comment suggested the OP is asking for), then your example S and the std Vec would both automatically derive Copy. Then, implementing Drop on them would become a compile error that you would have to silence by using the escape hatch to "un-derive" Copy from S/Vec.
So, whenever you wanted to implement Drop you'd need to engage the escape hatch.
So if you have some struct that you use extensively through an application and you need to extend it by adding a vector you are stuck because the change would need to touch so much code.
> But what's the point of the rules in this case, though? Here, the ownership rules does not prevent use after free, or double free, or data races, or any other bug. It's perfectly clear to a human that this code is fine and doesn't have any actual ownership issues
I mean, of course there is an obvious ownership issue with the code above, how are the destructors supposed to be ran without freeing the Id object twice?