diff --git a/site/blog/2025-07-04-derive-broken.md b/site/blog/2025-07-04-derive-broken.md deleted file mode 100644 index b052a12..0000000 --- a/site/blog/2025-07-04-derive-broken.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: "`#[derive(Clone)]` is broken" -description: "Not just `#[derive(Clone)]`, but all of the standard trait derives are" - -tags: -- rust ---- - -```rs -use std::sync::Arc; - -struct NoClone; - -#[derive(Clone)] -struct WrapArc(Arc); - -fn main() { - let foo = WrapArc(Arc::new(NoClone)); - let foo_ = foo.clone(); -} -``` - -Do you think this code should compile? - -What about the following code: - -```rs -struct AlwaysEq(T); - -impl PartialEq for AlwaysEq { - fn eq(&self, _other: &Self) -> bool { - true - } -} - -impl Eq for AlwaysEq {} - -struct NotEq; - -#[derive(PartialEq, Eq)] -struct WrapAlwaysEq(AlwaysEq); - -fn assert_is_eq(_: impl Eq) {} - -fn main() { - let x = WrapAlwaysEq(AlwaysEq(NotEq)); - assert_is_eq(x); -} -``` - -The second example is a bit far fetched, but you probably answered yes. - -But -[neither](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=0d9a89eccb4773ce32e7a3cec5cf8603) -[do](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=30233aea9cf20c3299cd5c1fc6e7ec8c). - -# Why not? - -The -[implementation of `#[derive(Clone)] in the Rust compiler`](https://github.com/rust-lang/rust/blob/0c4fa2690de945f062668acfc36b3f8cfbd013e2/compiler/rustc_builtin_macros/src/deriving/clone.rs) -generates a `Clone` implementation with the following requirements on the -derived type: - -- All fields must be `Clone`. -- All generic parameters must be `Clone`. - -Can you spot the issue here? It's the latter requirement: **we cannot just -require all generic parameters to be `Clone`, as we cannot assume they are used -in such a way that requires them to be cloned**.[^The reason this is the way it -is is probably because Rust's type system wasn't powerful enough for this to be -implemented back in the pre-1.0 days. Or it was just a simple oversight that got -stabilized.] - -This applies to practically all builtin derive traits, such as `Clone`, -`PartialEq`, `Eq`, or -[even `Debug`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=b419e34c9f00d0fca92c40739f6c9fb2). - -# What can we do to fix this? - -There are two solutions to this. Both require deleting that second requirement. - -## The hard way - -We could create a Rust RFC, hopefully not bikeshed it to death, and get it -stabilized in the next Rust edition as it is a breaking change.[^Surely it is a -breaking change, or the compiler people would've fixed it already. Right?] - -This would take 4+ years to stabilize and be available to everyone. That sucks, -but is the correct thing to do in the long-term. - -## The quick way - -We can just write our own macro that generates the following code: - -```rs -/* input */ -#[derive(CustomClone)] -struct WrapArc(Arc); - -/* generated code */ -impl Clone for WrapArc -where - Arc: Clone, - // and so on, `FieldType: DerivedTrait` for each field -{ - // ... -} -``` - -This does the job correctly. - -And it's not even hard to do. I know people who do this internally in their -company codebases - it's not much code. - -So I've [opened an issue](https://github.com/JelteF/derive_more/issues/490) -about replicating the builtin derive traits in a less restrictive and thus -correct way in the `derive_more` crate's GitHub repository. The reason I chose -this crate is because it already has a lot of users and is the main place for -derive implementations. - -Replicating already-existing behaviour of the std may not be in the scope of the -crate, which is a perfectly fine stance to take. If that doesn't get accepted, -I'll probably create my own crate and release it on -[crates.io](https://crates.io/). - -Stay tuned, I'll update this blog post.