Skip to content

No longer auto-implement Default for bitfields#1630

Draft
rsethc wants to merge 1 commit into
godot-rust:masterfrom
rsethc:specific-default-impl-for-some-bitfields
Draft

No longer auto-implement Default for bitfields#1630
rsethc wants to merge 1 commit into
godot-rust:masterfrom
rsethc:specific-default-impl-for-some-bitfields

Conversation

@rsethc

@rsethc rsethc commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

This was split off from: #1627

This would be a breaking change since any code that currently uses Default::default() and gets 0, for the types which are receiving a more specific implementation here, will get something different.

It will be a silently breaking change too (code that is broken by this change will likely still compile). It may be preferable to remove the derived Default, instead of implementing it differently, so that code broken by this change catches API users' attention much more noticeably.

Needs:

  • Consideration as to whether Default should even remain implemented (with the specialized implementation, as current) or whether the derive should simply be entirely removed instead.
  • To have tests added or updated?
  • To be squashed.

@Bromeon Bromeon added breaking-change Requires SemVer bump quality-of-life No new functionality, but improves ergonomics/internals labels Jun 9, 2026
@Bromeon Bromeon added this to the 0.6 milestone Jun 9, 2026
@Bromeon Bromeon added the c: engine Godot classes (nodes, resources, ...) label Jun 9, 2026
@GodotRust

Copy link
Copy Markdown

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1630

@Bromeon Bromeon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I added some comments.

Please also rebase, fix CI and squash commits to 1 🙂

Comment on lines +1364 to +1383
/// For types where a [`Default`](Default) is helpful, but the default value should
/// be something other than what would be returned by a `#[derive(Default)]` implementation.
///
/// An example of this is `DuplicateFlags`, where the default flags to `duplicate` are non-zero,
/// so if one wants to use "all flags except one" in `duplicate_ex`, the most natural way to do
/// so would be to pass `DuplicateFlags::default() & !DuplicateFlags::USE_INSTATANTIATION` to `flags`.
///
/// * `Some(TokenStream)` -> function body which will be psted into the `Default::default` implementation.
/// * `None` -> no special default, i.e. if a Default is generated it will be with an automatic derive.
#[rustfmt::skip]
pub fn does_enum_have_special_default(class_name: Option<&TyName>, enum_name: &str) -> Option<TokenStream> {
let class_name = class_name.map(|c| c.godot_ty.as_str());
match (class_name, enum_name) {
(Some("Node"), "DuplicateFlags") => Some(quote! {
Self::SIGNALS | Self::GROUPS | Self::SCRIPTS | Self::USE_INSTANTIATION
}),

_ => None
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use our line length of 140-145 for comments/RustDoc. Also fix typos like USE_INSTATANTIATION, but I think this may become obsolete, see other comment.

You can avoid the Some(...) by doing this:

let tokens = match (...) {
    (...) => quote! {
        ...
    },

    _ => return None,
}

Some(tokens)

if self.is_bitfield {
if self.is_bitfield && self.override_default.is_none() {
derives.push("Default");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should generate a Default without a manual implementation, there's just no good way to automate this in meaningful ways. It would also silently implement defaults for newly added bitfields, and changing them would then become breaking.

If we add impls manually through special_cases.rs, then:

  • We're sure the Default values are human-reviewed
  • We know that the ones listed there are actually useful (since we'll add new impls based on user requests)
  • There's no risk of breaking changes (once 0.6 is there), as we'll only add new impls.

This has implications beyond this condition:

  • "override" naming is no longer appropriate (since there won't be Default out of the box)
  • same with "special default", now there's just a "default"
  • docs in special_cases.rs need updating, too

@rsethc rsethc Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, due to the Default impl changing for types where it is not outright removed, this strategy makes it most clear to users of the crate?

  • 0.6.x: Remove the Default impls entirely, for types where it will change. Anyone upgrading will naturally have to refactor, to get their code to build, instead of having to diagnose strange issues where Default has quietly changed but code still compiles as-is.
  • 0.7.x: Re-add the Default impls for only types where there is a manual impl, but still ditch the derives.

In the 0.6.x case, a non-Default "meaningful_default" method could be impl'ed so that, even though we force people to look at code that we intentionally break, there is a holdover for Default that's nearly as obvious and convenient. Or just let people use the DEFAULT variant in case of enum bitfields.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping a cycle just means we'll have worse ergonomics during 0.6, and extra backlog for the future.

I expect users to read migration guides when upgrading minor versions, that's why we put a lot of effort into writing them. We also add deprecations wherever possible, but Rust doesn't support it for impls.

@Bromeon Bromeon changed the title Implement the Default trait with more specific code than the derived Default, for DuplicateFlags. No longer auto-implement Default for bitfields Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Requires SemVer bump c: engine Godot classes (nodes, resources, ...) quality-of-life No new functionality, but improves ergonomics/internals

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants