diff --git a/README.md b/README.md index e152f0a..6012932 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,29 @@ enum Enum { In the above case, `T` is bound to the `Debug` trait, but `K` is not. +Or, you can have `educe` replicate the behaviour of `std`'s `derive`'s, +where a bound is produced for *every* generic parameter, +without regard to how it's used in the structure: + +```rust +#[derive(Educe)] +#[educe(Debug(bound(*)))] +struct Struct { + #[educe(Debug(ignore))] + f: T, +} +``` + +This can be useful if you don't want to make the trait implementation +part of your permanent public API. +In this example, +`Struct` doesn't implement `Debug` unless `T` does. +I.e., it has a `T: Debug` bound even though that's not needed right now. +Later we might want to display `f`; we wouldn't then need to make +a breaking API change by adding the bound. + +This was the behaviour of `Trait(bound)` in educe 0.4.x and earlier. + ###### Union A union will be formatted as a `u8` slice because we don't know its fields at runtime. The fields of a union cannot be ignored, renamed, or formatted with other methods. The implementation is **unsafe** because it may expose uninitialized memory. diff --git a/src/common/bound.rs b/src/common/bound.rs index 1ca85f2..78f9aed 100644 --- a/src/common/bound.rs +++ b/src/common/bound.rs @@ -1,6 +1,7 @@ use syn::{punctuated::Punctuated, token::Comma, GenericParam, Meta, Path, Type, WherePredicate}; use crate::common::where_predicates_bool::{ + create_where_predicates_from_all_generic_parameters, create_where_predicates_from_generic_parameters_check_types, meta_2_where_predicates, WherePredicates, WherePredicatesOrBool, }; @@ -9,6 +10,7 @@ pub(crate) enum Bound { Disabled, Auto, Custom(WherePredicates), + All, } impl Bound { @@ -27,6 +29,7 @@ impl Bound { Self::Disabled } }, + WherePredicatesOrBool::All => Self::All, }) } } @@ -35,7 +38,7 @@ impl Bound { #[inline] pub(crate) fn into_where_predicates_by_generic_parameters_check_types( self, - _params: &Punctuated, + params: &Punctuated, bound_trait: &Path, types: &[&Type], supertraits: &[proc_macro2::TokenStream], @@ -48,6 +51,7 @@ impl Bound { supertraits, ), Self::Custom(where_predicates) => where_predicates, + Self::All => create_where_predicates_from_all_generic_parameters(params, bound_trait), } } } diff --git a/src/common/where_predicates_bool.rs b/src/common/where_predicates_bool.rs index 98e49a5..4aa3695 100644 --- a/src/common/where_predicates_bool.rs +++ b/src/common/where_predicates_bool.rs @@ -14,6 +14,7 @@ pub(crate) type WherePredicates = Punctuated; pub(crate) enum WherePredicatesOrBool { WherePredicates(WherePredicates), Bool(bool), + All, } impl WherePredicatesOrBool { @@ -42,6 +43,10 @@ impl Parse for WherePredicatesOrBool { return Self::from_lit(&lit); } + if let Ok(_star) = input.parse::() { + return Ok(Self::All); + } + Ok(Self::WherePredicates(input.parse_terminated(WherePredicate::parse, Token![,])?)) } } @@ -80,7 +85,6 @@ pub(crate) fn meta_2_where_predicates(meta: &Meta) -> syn::Result, bound_trait: &Path, diff --git a/tests/clone_struct.rs b/tests/clone_struct.rs index ffeeb80..077d198 100644 --- a/tests/clone_struct.rs +++ b/tests/clone_struct.rs @@ -1,6 +1,8 @@ #![cfg(feature = "Clone")] #![no_std] +use core::marker::PhantomData; + use educe::Educe; #[test] @@ -169,3 +171,31 @@ fn bound_3() { assert_eq!(1, s.f1); assert_eq!(1, t.0); } + +#[test] +fn bound_4() { + struct NotClone; + + #[derive(Educe)] + // without `bound(*)` we get E0034: multiple applicable items in scope + // when we call Struct.clone(), since .clone() is then ambiguous + #[educe(Clone(bound(*)))] + struct Struct { + f1: PhantomData, + } + + trait ClashingFakeClone { + fn clone(&self) {} + } + impl ClashingFakeClone for Struct {} + + let _: () = Struct { + f1: PhantomData:: + } + .clone(); + + let _: Struct<_> = Struct { + f1: PhantomData::<()> + } + .clone(); +}