Skip to content

Commit

Permalink
Add suppress_derive_clone struct attribute
Browse files Browse the repository at this point in the history
This attribute suppresses the default-generated `derive(Clone)`. This
may be necessary where `derive(Clone)` copies constraints in an
unsatisfiable way.

See: https://smallcultfollowing.com/babysteps//blog/2022/04/12/implied-bounds-and-perfect-derive/
See: rust-lang/rust#26925
Fixes: #325
  • Loading branch information
mathstuf committed Jun 27, 2024
1 parent 6408999 commit 3b55bb5
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 1 deletion.
5 changes: 5 additions & 0 deletions derive_builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,11 @@
//! }
//! ```
//!
//! The `Builder` will also automatically `derive(Clone)` in some cases. This may not always be
//! suitable if there are generic types involved. This may be suppressed using the
//! `suppress_derive_clone` attribute. The generated code may still need an `impl Clone for
//! Builder`; when using this attribute, it will need to be implemented in another way.
//!
//! Attributes declared for those traits are _not_ forwarded to the fields on the builder.
//!
//! ## Documentation Comments and Attributes
Expand Down
12 changes: 12 additions & 0 deletions derive_builder/tests/compile-fail/suppress_derive_clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[macro_use]
extern crate derive_builder;

#[derive(Builder)]
#[builder(suppress_derive_clone)]
pub struct Example {
field: String,
}

fn main() {
let _ = ExampleBuilder::default().clone();
}
40 changes: 40 additions & 0 deletions derive_builder/tests/manual_clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#[macro_use]
extern crate derive_builder;

#[derive(Debug, Builder, PartialEq, Clone)]
#[builder(suppress_derive_clone)]
struct Lorem {
#[builder(setter(into))]
ipsum: String,
}

impl Clone for LoremBuilder {
fn clone(&self) -> Self {
Self {
ipsum: self.ipsum.clone(),
}
}
}

#[test]
fn error_if_uninitialized() {
let error = LoremBuilder::default().build().unwrap_err();
assert_eq!(&error.to_string(), "`ipsum` must be initialized");
}

#[test]
fn builder_test() {
let x = LoremBuilder::default().ipsum("ipsum").build().unwrap();

assert_eq!(
x,
Lorem {
ipsum: "ipsum".into()
}
);
}

#[test]
fn builder_is_clone() {
let _ = LoremBuilder::default().clone();
}
7 changes: 6 additions & 1 deletion derive_builder_core/src/macro_options/darling_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,11 @@ pub struct Options {

#[darling(skip, default)]
deprecation_notes: DeprecationNotes,

/// Suppress the `derive(Clone)` attribute.
///
/// If `Clone` is needed, it must be generated in some other way.
suppress_derive_clone: Flag,
}

/// Accessors for parsed properties.
Expand Down Expand Up @@ -687,7 +692,7 @@ impl Options {
.map(|e| *e.validation_error)
.unwrap_or(true),
no_alloc: cfg!(not(any(feature = "alloc", feature = "lib_has_std"))),
must_derive_clone: self.requires_clone(),
must_derive_clone: self.requires_clone() && !self.suppress_derive_clone.is_present(),
doc_comment: None,
deprecation_notes: Default::default(),
std: !self.no_std.is_present(),
Expand Down

0 comments on commit 3b55bb5

Please sign in to comment.