Repository navigation
Implement Default聽#4975
Description
Activity
- added a commit that references this issue
on Feb 14, 2026 - added a commit that references this issue
on Feb 15, 2026 I think ideally we would accept a
#[skip_derive_default]attribute ins!which forwards to aimpl_default!macro. That macro could then look for acustom_defaultattribute use its contents rather thanDefault::default().For example, this:
s! { #[skip_derive_default] struct foo { pub a: i32, #[custom_default([0u8; 1024])] pub b: [u8; 1024], #[custom_default(bar { a: 0 })] pub bar: bar, } union bar { a: i32, b: f32, } }
Expands to this:
#[derive(Clone, Copy, Debug, ....)] strip_custom_attributes! { struct foo { pub a: i32, #[custom_default([0u8; 1024])] pub b: [u8; 1024], #[custom_default(bar { a: 0 })] pub bar: bar, } } custom_default! { struct foo { pub a: i32, #[custom_default([0u8; 1024])] pub b: [u8; 1024], #[custom_default(bar { a: 0 })] pub bar: bar, } } // ignoring `union bar`
Which then expands to:
#[derive(Clone, Copy, Debug, ....)] struct foo { pub a: i32, pub b: [u8; 1024], pub bar: bar, } impl Default for foo { fn default() -> Self { Self { // For fields without `custom_default`, just use `Default::default()` a: Default::default(), // For `custom_default`, use that token tree b: [0u8; 1024], bar: bar { a: 0 }, } } } // ignoring `union bar`
This isn't going to be the most fun macro to write, but it's much preferred to dealing with hundreds of custom derives.
I won't be working on this for a while but can provide guidance if anybody is interested.
Reacted by Yuki Okushi- addedE-mediumE-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate.E-medium Call for participation: Medium difficulty. Experience needed to fix: Intermediate.E-help-wantedCall for participation: Help is requested to fix this issue.Call for participation: Help is requested to fix this issue.
on Feb 16, 2026 - added a commit that references this issue
on Feb 16, 2026 - added a commit that references this issue
on Mar 8, 2026 - added a commit that references this issue
on Mar 8, 2026 Mind if I take this? I messed around with a prototype
impl_default!that generates the impl field by field.Default::default()for normal fields,#[custom_default(...)]for arrays larger than 32, andmem::zeroed()for unions. I also gots!dispatching off a#[skip_derive_default]marker. Tested it on a plain struct, the big arrays, a union, and a struct containing a union, all passing.Two questions. Is zeroing unions ok when there's no
custom_default? Seems right for the plain C unions but wanted to check. Also, do you want Default on everything by default eventually, or keep it opt-in per struct? Can't really flip it on globally without breaking every big-array struct until they're annotated, so I've got it opt-in for now.Only limitation is that
custom_defaulthas to be the first attribute on a field since the macro matches literally. Doesn't seem like it'd be a problem, but worth mentioning.Mind if I take this? I messed around with a prototype
impl_default!that generates the impl field by field.Default::default()for normal fields,#[custom_default(...)]for arrays larger than 32, andmem::zeroed()for unions. I also gots!dispatching off a#[skip_derive_default]marker. Tested it on a plain struct, the big arrays, a union, and a struct containing a union, all passing.Help would be very welcome! Especially if you have it working already, that's amazing.
Two questions. Is zeroing unions ok when there's no
custom_default? Seems right for the plain C unions but wanted to check.In general using
mem::zeroedshould be okay for alllibctypes, and is probably actually preferable for unions so we don't need to remember what the largest field is. But since this operation still requires asserting that zeros are a valid bitpattern, I'd like to haveunsafebe needed somewhere in the macro.Could something like this work in the macro?
#[unsafe(default_via_zeroed)] union bar { a: i32, b: f32, }
I'd recommend doing that later though, and just starting with
#[custom_default(unsafe { mem::zeroed<some_union>() })]the few places it's needed.Also, do you want Default on everything by default eventually, or keep it opt-in per struct? Can't really flip it on globally without breaking every big-array struct until they're annotated, so I've got it opt-in for now.
Eventually on by default is the plan, but starting small sounds great. Could you do this by introducing a
s_with_default!macro so we can do whole blocks?Only limitation is that
custom_defaulthas to be the first attribute on a field since the macro matches literally. Doesn't seem like it'd be a problem, but worth mentioning.That's pretty much always been my experience writing these kind of hacky macros, it's not worth the complexity to allow anything default.
You might have this already but make sure you have some unit tests. Basically just a dummy
s!invocation then pass the type to afn assert_impls_default<T: Default>() {}.In case it helps, here's one such macro I wrote for a syn-like crate. That one has extra complexity because it does different things with fields before and after
#[group], but it's pretty nicely organized and commented https://gist.github.com/tgross35/b4c87129601ff336b6efe325c4dd5771.Thanks for the detailed feedback! This is all very helpful. I'll get a PR up with the macro shortly.
Reacted by Trevor Gross
Discussed at #t-libs > Questions and polls about `libc` 1.0 @ 馃挰, we would like to have a
Defaultimpl for pretty much everything. This should be part ofs!/s_no_extra_traits!but won't work out of the box with a derive.In particular, we need a workaround for
[T; N]whereN > 32, and unions somehow.