Skip to content

Fix PartialReflect::apply for maps, remove get_at/_mut from Map trait #19802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 25, 2025

Conversation

Azorlogh
Copy link
Contributor

@Azorlogh Azorlogh commented Jun 24, 2025

Objective

  • Fixes Remove get_at method from Map trait (reflection) #14328
  • DynamicMap::drain was broken (indices weren't cleared, causing a panic when reading later)
  • PartialReflect::apply was broken for maps and sets, because they don't remove entries from the self map that aren't in the applied map.
    • I discovered this bug when implementing MapEntities on a Component containing a HashMap<Entity, _>. Because apply is used to reapply the changes to the reflected map, the map ended up littered with a ton of outdated entries.

Solution

  • Remove the separate Vec in DynamicMap and use the HashTable directly, like it is in DynamicSet.
  • Replace MapIter by Box<dyn Iterator> (like for DynamicSet), and Map::get_at and Map::get_at_mut which are now unused.
  • Now assume DynamicMap types are unordered and adjust documentation accordingly.
  • Fix documentation of DynamicSet (ordered -> unordered)
  • Added Map::retain and Set::retain, and use them to remove excess entries in PartialReflect::apply implementations.

Testing

  • Added map::tests::apply and set::tests::apply to validate <DynamicMap as PartialReflect>::apply and <DynamicSet as PartialReflect>::apply

@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-Reflection Runtime information about types S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 24, 2025
@alice-i-cecile alice-i-cecile requested a review from MrGVSV June 24, 2025 20:55
@alice-i-cecile
Copy link
Member

@RobWalt, can I get your review here too?

@alice-i-cecile alice-i-cecile added the M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Jun 24, 2025
Copy link
Contributor

It looks like your PR is a breaking change, but you didn't provide a migration guide.

Please review the instructions for writing migration guides, then expand or revise the content in the migration guides directory to reflect your changes.

@alice-i-cecile
Copy link
Member

I'm broadly happy with this, but it needs a migration guide :)

Copy link
Contributor

@RobWalt RobWalt left a comment

Choose a reason for hiding this comment

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

Looks great! Also really nice docs 💯 It was a pleasant review :)

@@ -1583,7 +1583,6 @@ mod tests {
foo.apply(&foo_patch);

let mut hash_map = <HashMap<_, _>>::default();
hash_map.insert(1, 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch! How did this not lead to a failed test before? o.O

I mean it kind of makes sense that this code

        let mut map = DynamicMap::default();
        map.insert(2usize, 3i8);
        map.insert(3usize, 4i8);
        foo_patch.insert("d", map);

replaces the old map completely and hence doesn't include the (1,1) entry. The change here makes me wonder how it worked before though. The test makes it seem like previously the (1,1) entry would have been preserved.

I probably need to look a bit further into the changes..

@@ -615,117 +554,17 @@ pub fn map_try_apply<M: Map>(a: &mut M, b: &dyn PartialReflect) -> Result<(), Ap
a.insert_boxed(key.to_dynamic(), b_value.to_dynamic());
}
}
a.retain(&mut |key, _| map_value.get(key).is_some());
Copy link
Contributor

Choose a reason for hiding this comment

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

Ahhh I guess that's where the removed entry (1,1) comes from.

Yeah I also second @alice-i-cecile message. We should really highlight this in a migration guide as, if you're used to the previous behavior, it seems like a big trip wire since it's so implicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review! I added the migration guide with all the behavior changes.
I would consider the non-removal of the extra entry a bug so hopefully no one is actually relying on that in the wild! 😅

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 25, 2025
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Jun 25, 2025
Merged via the queue into bevyengine:main with commit dd4479e Jun 25, 2025
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Reflection Runtime information about types C-Bug An unexpected or incorrect behavior M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove get_at method from Map trait (reflection)
4 participants