Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for deriving Clone/Copy and other standard traits without generic type parameter transitiveness #439

Open
sandersaares opened this issue Jan 17, 2025 · 1 comment
Assignees
Milestone

Comments

@sandersaares
Copy link

sandersaares commented Jan 17, 2025

The built-in #[derive(Copy, Clone)] and others in Rust have this rule that all generic type parameters must also implement Copy or Clone. Perhaps the logic relies on this to ensure that all fields really can be copied/cloned/compared?

However, this is not always desirable. For example, some generic type parameter might simply be used by reference:

trait Bar {}

#[derive(Copy, Clone)]
struct Foo<B: Bar> {
    bar: &'static B
}

While the reference bar is perfectly copyable, Rust will complain here and say that Copy and Clone methods cannot be called because B: Clone is not satisfied: Rust Playground.

error[E0599]: the method `clone` exists for struct `Foo<BarImpl>`, but its trait bounds were not satisfied
  --> src\main.rs:14:20
   |
4  | struct Foo<B: Bar> {
   | ------------------ method `clone` not found for this struct because it doesn't satisfy `Foo<BarImpl>: Clone`
...
8  | struct BarImpl;
   | -------------- doesn't satisfy `BarImpl: Clone`
...
14 |     let foo2 = foo.clone();
   |                    ^^^^^ method cannot be called on `Foo<BarImpl>` due to unsatisfied trait bounds
   |
note: trait bound `BarImpl: Clone` was not satisfied
  --> src\main.rs:3:16
   |
3  | #[derive(Copy, Clone)]
   |                ^^^^^ unsatisfied trait bound introduced in this `derive` macro
help: consider annotating `BarImpl` with `#[derive(Clone)]`
   |
8  + #[derive(Clone)]
9  | struct BarImpl;

However, this check is excessively restrictive. Manually implementing Copy and Clone works fine because the type B is only used as a reference.

impl<B: Bar> Copy for Foo<B> {}
impl<B: Bar> Clone for Foo<B> {
    fn clone(&self) -> Self {
        Self { bar: self.bar.clone() }
    }
}

The above compiles successfully and copies/clones the reference regardless of whether B itself impelments any of these traits.

trait Bar {}

#[derive(CopyMore, CloneMore)]
struct Foo<B: Bar> {
    bar: &'static B
}

It would be very useful to have "explicit" variations of the built-in derive-macros for such situations, so we could #[derive(CopyMore, CloneMore, EqMore)] and just have an implementation generated that goes field by field without looking at the types and leaves it up to the compiler to detect actual field by field compatibility in the generated code.

@tyranron tyranron self-assigned this Jan 17, 2025
@tyranron tyranron added this to the 2.1.0 milestone Jan 17, 2025
@tyranron
Copy link
Collaborator

@sandersaares thanks for bringing this up. This was definitely on my agenda, though not prioritized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants