Fix Map/List/Set equality in generated Dart structs#2956
Fix Map/List/Set equality in generated Dart structs#2956nightscape wants to merge 1 commit intofzyzcjy:masterfrom
Conversation
Generated Dart structs with collection fields (Map, List, Set) were using identity comparison (==) instead of deep equality. This caused equality checks to fail even when collection contents were identical. Fix: - Use DeepCollectionEquality for collection fields in operator== and hashCode - Export DeepCollectionEquality from flutter_rust_bridge_for_generated_common.dart - Handle Optional types wrapping collections
There was a problem hiding this comment.
Pull request overview
This pull request fixes a critical bug in generated Dart struct equality checking where collection fields (Map, List, Set) were using identity comparison (==) instead of deep equality comparison. This caused equality checks to fail even when collection contents were identical.
Key Changes:
- Modified code generation logic to use
DeepCollectionEqualityfor collection fields inoperator==andhashCode - Added recursive handling for Optional types wrapping collections
- Exported
DeepCollectionEqualityfrom the common library for generated code
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
frb_codegen/src/library/codegen/generator/api_dart/spec_generator/class/ty/structure_non_freezed.rs |
Core logic change: Added needs_deep_equality() function to detect collection types and generate appropriate equality/hash code using DeepCollectionEquality |
frb_dart/lib/flutter_rust_bridge_for_generated_common.dart |
Exported DeepCollectionEquality from collection package for use in generated code |
frb_example/pure_dart/test/api/mirror_test.dart |
Added tests verifying Map and List equality works correctly for structs with identical contents |
frb_example/pure_dart/lib/src/rust/api/*.dart |
Generated Dart code updated to use DeepCollectionEquality for List/Map/Set fields in equality and hashCode methods |
frb_example/pure_dart/lib/src/rust/api/enumeration.dart |
Code formatting changes (unrelated to equality fix) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| delegate, | ||
| MirTypeDelegate::Map(_) | MirTypeDelegate::Set(_) | ||
| ), | ||
| MirType::Optional(opt) => needs_deep_equality(&opt.inner), |
There was a problem hiding this comment.
The needs_deep_equality function does not handle MirType::Boxed types that wrap collections (List, Map, or Set). If a field has type Box<List<T>>, Box<Map<K,V>>, or Box<Set<T>>, the function will return false and use identity comparison instead of deep equality.
Consider adding a case for MirType::Boxed that recursively checks the inner type, similar to how MirType::Optional is handled.
| MirType::Optional(opt) => needs_deep_equality(&opt.inner), | |
| MirType::Optional(opt) => needs_deep_equality(&opt.inner), | |
| MirType::Boxed(_) => true, |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2956 +/- ##
===========================================
- Coverage 98.57% 85.19% -13.39%
===========================================
Files 464 464
Lines 19202 18976 -226
===========================================
- Hits 18928 16166 -2762
- Misses 274 2810 +2536 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Great job! However, this seems to be a breaking change, maybe we should add a flag to allow users to enable/disable it. In addition, it seems that users can use |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request aims to fix an issue where Dart structs with collection fields used identity comparison instead of deep equality. The approach of using DeepCollectionEquality from the collection package is correct and has been applied to the code generator for hashCode and operator==. The changes also include adding new tests to verify this fix for Map and List types. However, the implementation in the code generator is incomplete as it does not handle all collection types, such as primitive lists (e.g., Uint8List) and arrays. I've left a specific comment with a suggestion to address this.
| fn needs_deep_equality(ty: &MirType) -> bool { | ||
| match ty { | ||
| MirType::GeneralList(_) => true, | ||
| MirType::Delegate(delegate) => matches!( | ||
| delegate, | ||
| MirTypeDelegate::Map(_) | MirTypeDelegate::Set(_) | ||
| ), | ||
| MirType::Optional(opt) => needs_deep_equality(&opt.inner), | ||
| _ => false, | ||
| } | ||
| } |
There was a problem hiding this comment.
The needs_deep_equality function seems to be incomplete. It doesn't handle all collection types, which will lead to incorrect equality checks for structs containing them.
Specifically, it's missing:
MirType::PrimitiveList(_): For typed data lists likeUint8List,Int32List, etc.MirTypeDelegate::Array(_): For fixed-size arrays.
These types, like other collections, are compared by identity by default in Dart, so they require deep equality checks for correctness. The DeepCollectionEquality from the collection package handles these cases correctly.
I suggest updating the function to include these types.
| fn needs_deep_equality(ty: &MirType) -> bool { | |
| match ty { | |
| MirType::GeneralList(_) => true, | |
| MirType::Delegate(delegate) => matches!( | |
| delegate, | |
| MirTypeDelegate::Map(_) | MirTypeDelegate::Set(_) | |
| ), | |
| MirType::Optional(opt) => needs_deep_equality(&opt.inner), | |
| _ => false, | |
| } | |
| } | |
| fn needs_deep_equality(ty: &MirType) -> bool { | |
| match ty { | |
| MirType::GeneralList(_) | MirType::PrimitiveList(_) => true, | |
| MirType::Delegate(delegate) => matches!( | |
| delegate, | |
| MirTypeDelegate::Array(_) | MirTypeDelegate::Map(_) | MirTypeDelegate::Set(_) | |
| ), | |
| MirType::Optional(opt) => needs_deep_equality(&opt.inner), | |
| _ => false, | |
| } | |
| } |
Generated Dart structs with collection fields (Map, List, Set) were using identity comparison (==) instead of deep equality. This caused equality checks to fail even when collection contents were identical.
Fix:
Changes
Fixes #the-issue-number-here
Procedure and Checklist
In order to quickly iterate and avoid slowing down development pace by making CI pass, only the following simplified steps are needed, and I (fzyzcjy) will handle the rest of CI / moving the tests currently (will have more automation in the future).
frb_example/dart_minimal.dart_minimal's CI green.Utility commands
cargo run --manifest-path /path/to/flutter_rust_bridge/frb_codegen/Cargo.toml -- generate./frb_internal test-dart-native --package frb_example/dart_minimal