Skip to content

Commit 9e23e34

Browse files
committed
Merge branch 'ijackson-all-bounds'
2 parents fca9263 + b7567cb commit 9e23e34

File tree

4 files changed

+63
-2
lines changed

4 files changed

+63
-2
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,29 @@ enum Enum<T, K> {
211211

212212
In the above case, `T` is bound to the `Debug` trait, but `K` is not.
213213

214+
Or, you can have `educe` replicate the behaviour of `std`'s `derive`'s,
215+
where a bound is produced for *every* generic parameter,
216+
without regard to how it's used in the structure:
217+
218+
```rust
219+
#[derive(Educe)]
220+
#[educe(Debug(bound(*)))]
221+
struct Struct<T> {
222+
#[educe(Debug(ignore))]
223+
f: T,
224+
}
225+
```
226+
227+
This can be useful if you don't want to make the trait implementation
228+
part of your permanent public API.
229+
In this example,
230+
`Struct<T>` doesn't implement `Debug` unless `T` does.
231+
I.e., it has a `T: Debug` bound even though that's not needed right now.
232+
Later we might want to display `f`; we wouldn't then need to make
233+
a breaking API change by adding the bound.
234+
235+
This was the behaviour of `Trait(bound)` in educe 0.4.x and earlier.
236+
214237
###### Union
215238

216239
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.

src/common/bound.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use syn::{punctuated::Punctuated, token::Comma, GenericParam, Meta, Path, Type, WherePredicate};
22

33
use crate::common::where_predicates_bool::{
4+
create_where_predicates_from_all_generic_parameters,
45
create_where_predicates_from_generic_parameters_check_types, meta_2_where_predicates,
56
WherePredicates, WherePredicatesOrBool,
67
};
@@ -9,6 +10,7 @@ pub(crate) enum Bound {
910
Disabled,
1011
Auto,
1112
Custom(WherePredicates),
13+
All,
1214
}
1315

1416
impl Bound {
@@ -27,6 +29,7 @@ impl Bound {
2729
Self::Disabled
2830
}
2931
},
32+
WherePredicatesOrBool::All => Self::All,
3033
})
3134
}
3235
}
@@ -35,7 +38,7 @@ impl Bound {
3538
#[inline]
3639
pub(crate) fn into_where_predicates_by_generic_parameters_check_types(
3740
self,
38-
_params: &Punctuated<GenericParam, Comma>,
41+
params: &Punctuated<GenericParam, Comma>,
3942
bound_trait: &Path,
4043
types: &[&Type],
4144
supertraits: &[proc_macro2::TokenStream],
@@ -48,6 +51,7 @@ impl Bound {
4851
supertraits,
4952
),
5053
Self::Custom(where_predicates) => where_predicates,
54+
Self::All => create_where_predicates_from_all_generic_parameters(params, bound_trait),
5155
}
5256
}
5357
}

src/common/where_predicates_bool.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub(crate) type WherePredicates = Punctuated<WherePredicate, Token![,]>;
1414
pub(crate) enum WherePredicatesOrBool {
1515
WherePredicates(WherePredicates),
1616
Bool(bool),
17+
All,
1718
}
1819

1920
impl WherePredicatesOrBool {
@@ -42,6 +43,10 @@ impl Parse for WherePredicatesOrBool {
4243
return Self::from_lit(&lit);
4344
}
4445

46+
if let Ok(_star) = input.parse::<Token![*]>() {
47+
return Ok(Self::All);
48+
}
49+
4550
Ok(Self::WherePredicates(input.parse_terminated(WherePredicate::parse, Token![,])?))
4651
}
4752
}
@@ -80,7 +85,6 @@ pub(crate) fn meta_2_where_predicates(meta: &Meta) -> syn::Result<WherePredicate
8085
}
8186

8287
#[inline]
83-
#[allow(dead_code)]
8488
pub(crate) fn create_where_predicates_from_all_generic_parameters(
8589
params: &Punctuated<GenericParam, Comma>,
8690
bound_trait: &Path,

tests/clone_struct.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![cfg(feature = "Clone")]
22
#![no_std]
33

4+
use core::marker::PhantomData;
5+
46
use educe::Educe;
57

68
#[test]
@@ -169,3 +171,31 @@ fn bound_3() {
169171
assert_eq!(1, s.f1);
170172
assert_eq!(1, t.0);
171173
}
174+
175+
#[test]
176+
fn bound_4() {
177+
struct NotClone;
178+
179+
#[derive(Educe)]
180+
// without `bound(*)` we get E0034: multiple applicable items in scope
181+
// when we call Struct<NotClone>.clone(), since .clone() is then ambiguous
182+
#[educe(Clone(bound(*)))]
183+
struct Struct<T> {
184+
f1: PhantomData<T>,
185+
}
186+
187+
trait ClashingFakeClone {
188+
fn clone(&self) {}
189+
}
190+
impl ClashingFakeClone for Struct<NotClone> {}
191+
192+
let _: () = Struct {
193+
f1: PhantomData::<NotClone>
194+
}
195+
.clone();
196+
197+
let _: Struct<_> = Struct {
198+
f1: PhantomData::<()>
199+
}
200+
.clone();
201+
}

0 commit comments

Comments
 (0)