This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new a7023de04 feat(rust): avoid downcast method of multiple trait objects
in same module conflict (#2708)
a7023de04 is described below
commit a7023de0435088e34d51fcb75258af45caeb178d
Author: Shawn Yang <[email protected]>
AuthorDate: Mon Oct 6 19:50:05 2025 +0800
feat(rust): avoid downcast method of multiple trait objects in same module
conflict (#2708)
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
fix multiple trait objects in same module `from_any_internal` conflict
## Related issues
#2691
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
Delete section if not applicable.
-->
---
rust/README.md | 32 +++++--
rust/fory-core/src/resolver/ref_resolver.rs | 120 +-------------------------
rust/fory-core/src/serializer/any.rs | 4 +-
rust/fory-core/src/serializer/trait_object.rs | 62 +++++++------
rust/fory-derive/src/object/read.rs | 20 +++--
rust/fory-derive/src/object/util.rs | 6 +-
rust/fory-derive/src/object/write.rs | 4 +-
rust/fory-derive/src/util.rs | 14 ++-
rust/tests/tests/test_trait_object.rs | 67 ++++++++++++++
9 files changed, 159 insertions(+), 170 deletions(-)
diff --git a/rust/README.md b/rust/README.md
index 6c74498e7..3b7c9fd76 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -198,24 +198,44 @@ assert_eq!(deserialized.animals_arc[0].speak(), "Woof!");
**Wrapper Types for Standalone Usage**
-Due to Rust's orphan rule, `Rc<dyn Trait>` and `Arc<dyn Trait>` cannot
implement `Serializer` directly. For standalone serialization (not inside
struct fields), use the fory auto-generated wrapper types:
+Due to Rust's orphan rule, `Rc<dyn Trait>` and `Arc<dyn Trait>` cannot
implement `Serializer` directly. For standalone serialization (not inside
struct fields), use the `Rc<dyn Any>/Arc<dyn Any>` or fory auto-generated
wrapper types instead:
+
+Example for `Rc<dyn T>`:
+
+```rust
+let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden".to_string()
+});
+let dog = doc_rc.as_any();
+
+// Serialize the wrapper
+let serialized = fory.serialize(&wrapper);
+let deserialized: Rc<dyn Any> = fory.deserialize(&serialized)?;
+
+// Unwrap back to Rc<dyn Animal>
+let unwrapped: Rc<dyn Animal> = deserialized.downcast_ref::<Dog>();
+assert_eq!(unwrapped.name(), "Rex");
+```
+
+Example for `Arc<dyn Any>`:
```rust
// register_trait_type! generates: AnimalRc and AnimalArc
// Wrap Rc/Arc trait objects
-let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
+let dog_rc: Arc<dyn Animal> = Arc::new(Dog {
name: "Rex".to_string(),
breed: "Golden".to_string()
});
-let wrapper = AnimalRc::from(dog_rc);
+let wrapper = AnimalArc::from(dog_rc);
// Serialize the wrapper
let serialized = fory.serialize(&wrapper);
-let deserialized: AnimalRc = fory.deserialize(&serialized)?;
+let deserialized: AnimalArc = fory.deserialize(&serialized)?;
-// Unwrap back to Rc<dyn Animal>
-let unwrapped: Rc<dyn Animal> = deserialized.unwrap();
+// Unwrap back to Arc<dyn Animal>
+let unwrapped: Arc<dyn Animal> = deserialized.unwrap();
assert_eq!(unwrapped.name(), "Rex");
```
diff --git a/rust/fory-core/src/resolver/ref_resolver.rs
b/rust/fory-core/src/resolver/ref_resolver.rs
index 458e8d7d2..5c1c87e74 100644
--- a/rust/fory-core/src/resolver/ref_resolver.rs
+++ b/rust/fory-core/src/resolver/ref_resolver.rs
@@ -201,7 +201,7 @@ impl RefReader {
ref_id
}
- /// Get an Rc<T> by reference ID during deserialization.
+ /// Get an `Rc<T>` by reference ID during deserialization.
///
/// # Arguments
///
@@ -216,7 +216,7 @@ impl RefReader {
any_box.downcast_ref::<Rc<T>>().cloned()
}
- /// Get an Arc<T> by reference ID during deserialization.
+ /// Get an `Arc<T>` by reference ID during deserialization.
///
/// # Arguments
///
@@ -275,119 +275,3 @@ impl RefReader {
self.refs.clear();
}
}
-
-/// Legacy RefResolver that combines both reading and writing functionality.
-///
-/// This type is maintained for backward compatibility but it's recommended
-/// to use RefWriter and RefReader separately for better separation of
concerns.
-///
-/// # Deprecated
-///
-/// Use RefWriter for serialization and RefReader for deserialization instead.
-#[deprecated(note = "Use RefWriter for serialization and RefReader for
deserialization")]
-#[derive(Default)]
-pub struct RefResolver {
- /// Maps pointer addresses to reference IDs for serialization
- write_refs: HashMap<usize, u32>,
- /// Vector to store boxed objects for deserialization
- read_refs: Vec<Box<dyn Any>>,
- /// Next reference ID to assign
- next_ref_id: u32,
-}
-
-#[allow(deprecated)]
-impl RefResolver {
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Attempt to write a reference for an Rc<T>. Returns true if reference
was written,
- /// false if this is the first occurrence and should be serialized
normally.
- pub fn try_write_rc_ref<T: ?Sized>(&mut self, writer: &mut Writer, rc:
&Rc<T>) -> bool {
- let ptr_addr = Rc::as_ptr(rc) as *const () as usize;
-
- if let Some(&ref_id) = self.write_refs.get(&ptr_addr) {
- // This object has been seen before, write a reference
- writer.write_i8(RefFlag::Ref as i8);
- writer.write_u32(ref_id);
- true
- } else {
- // First time seeing this object, register it and return false
- let ref_id = self.next_ref_id;
- self.next_ref_id += 1;
- self.write_refs.insert(ptr_addr, ref_id);
- writer.write_i8(RefFlag::RefValue as i8);
- false
- }
- }
-
- /// Attempt to write a reference for an Arc<T>. Returns true if reference
was written,
- /// false if this is the first occurrence and should be serialized
normally.
- pub fn try_write_arc_ref<T: ?Sized>(&mut self, writer: &mut Writer, arc:
&Arc<T>) -> bool {
- let ptr_addr = Arc::as_ptr(arc) as *const () as usize;
-
- if let Some(&ref_id) = self.write_refs.get(&ptr_addr) {
- // This object has been seen before, write a reference
- writer.write_i8(RefFlag::Ref as i8);
- writer.write_u32(ref_id);
- true
- } else {
- // First time seeing this object, register it and return false
- let ref_id = self.next_ref_id;
- self.next_ref_id += 1;
- self.write_refs.insert(ptr_addr, ref_id);
- writer.write_i8(RefFlag::RefValue as i8);
- false
- }
- }
-
- /// Store an Rc<T> for later reference resolution during deserialization
- pub fn store_rc_ref<T: 'static + ?Sized>(&mut self, rc: Rc<T>) -> u32 {
- let ref_id = self.read_refs.len() as u32;
- self.read_refs.push(Box::new(rc));
- ref_id
- }
-
- /// Store an Arc<T> for later reference resolution during deserialization
- pub fn store_arc_ref<T: 'static + ?Sized>(&mut self, arc: Arc<T>) -> u32 {
- let ref_id = self.read_refs.len() as u32;
- self.read_refs.push(Box::new(arc));
- ref_id
- }
-
- /// Get an Rc<T> by reference ID during deserialization
- pub fn get_rc_ref<T: 'static + ?Sized>(&self, ref_id: u32) ->
Option<Rc<T>> {
- let any_box = self.read_refs.get(ref_id as usize)?;
- any_box.downcast_ref::<Rc<T>>().cloned()
- }
-
- /// Get an Arc<T> by reference ID during deserialization
- pub fn get_arc_ref<T: 'static + ?Sized>(&self, ref_id: u32) ->
Option<Arc<T>> {
- let any_box = self.read_refs.get(ref_id as usize)?;
- any_box.downcast_ref::<Arc<T>>().cloned()
- }
-
- /// Read a reference flag and determine what action to take
- pub fn read_ref_flag(&self, reader: &mut Reader) -> RefFlag {
- let flag_value = reader.read_i8();
- match flag_value {
- -3 => RefFlag::Null,
- -2 => RefFlag::Ref,
- -1 => RefFlag::NotNullValue,
- 0 => RefFlag::RefValue,
- _ => panic!("Invalid reference flag: {}", flag_value),
- }
- }
-
- /// Read a reference ID
- pub fn read_ref_id(&self, reader: &mut Reader) -> u32 {
- reader.read_u32()
- }
-
- /// Clear all stored references (useful for reusing the resolver)
- pub fn clear(&mut self) {
- self.write_refs.clear();
- self.read_refs.clear();
- self.next_ref_id = 0;
- }
-}
diff --git a/rust/fory-core/src/serializer/any.rs
b/rust/fory-core/src/serializer/any.rs
index 98931cd9f..6ada17f55 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -24,7 +24,7 @@ use std::any::Any;
use std::rc::Rc;
use std::sync::Arc;
-/// Helper function to serialize a Box<dyn Any>
+/// Helper function to serialize a `Box<dyn Any>`
pub fn serialize_any_box(any_box: &Box<dyn Any>, context: &mut WriteContext,
is_field: bool) {
context.writer.write_i8(RefFlag::NotNullValue as i8);
@@ -34,7 +34,7 @@ pub fn serialize_any_box(any_box: &Box<dyn Any>, context:
&mut WriteContext, is_
serializer_fn(&**any_box, context, is_field);
}
-/// Helper function to deserialize to Box<dyn Any>
+/// Helper function to deserialize to `Box<dyn Any>`
pub fn deserialize_any_box(context: &mut ReadContext) -> Result<Box<dyn Any>,
Error> {
let ref_flag = context.reader.read_i8();
if ref_flag != RefFlag::NotNullValue as i8 {
diff --git a/rust/fory-core/src/serializer/trait_object.rs
b/rust/fory-core/src/serializer/trait_object.rs
index 7269c8769..d1d23e835 100644
--- a/rust/fory-core/src/serializer/trait_object.rs
+++ b/rust/fory-core/src/serializer/trait_object.rs
@@ -103,12 +103,16 @@ macro_rules! resolve_and_deserialize {
/// Macro to register trait object conversions for custom traits.
///
/// This macro automatically generates serializers for `Box<dyn Trait>` trait
objects.
-/// Due to Rust's orphan rules, only Box<dyn Trait> is supported for
user-defined traits.
+/// Due to Rust's orphan rules, only `Box<dyn Trait>` is supported for
user-defined traits.
+/// For `Rc<dyn Trait>` and `Arc<dyn Trait>`, wrapper types are generated
(e.g., `TraitRc`, `TraitArc`),
+/// either you use the wrapper types or use the `Rc<dyn Any>` or `Arc<dyn
Any>` instead if it's not
+/// inside struct fields. For struct fields, you can use the `Rc<dyn Trait>`,
`Arc<dyn Trait>` directly,
+/// fory will generate converters for `Rc<dyn Trait>` and `Arc<dyn Trait>` to
convert to wrapper for
+/// serialization/ deserialization automatically.
///
/// The macro generates:
/// - `Serializer` implementation for `Box<dyn Trait>`
/// - `Default` implementation for `Box<dyn Trait>` (uses first registered
type)
-/// - `from_any_internal()` helper for deserializing trait objects
///
/// **Note**: Your trait must extend the `Serializer` trait.
/// The `as_any()` method is automatically provided by the `Serializer` trait.
@@ -238,29 +242,31 @@ macro_rules! register_trait_type {
}
}
- // Create helper functions for this trait
- #[allow(non_snake_case)]
- mod __fory_trait_helpers {
- use super::*;
-
- #[allow(dead_code)]
- pub fn from_any_internal(
- any_box: Box<dyn std::any::Any>,
- _fory_type_id: u32,
- ) -> Result<Box<dyn $trait_name>, $crate::error::Error> {
- $(
- if any_box.is::<$impl_type>() {
- let concrete = any_box.downcast::<$impl_type>()
- .map_err(|_| $crate::error::Error::Other(
-
$crate::error::AnyhowError::msg(format!("Failed to downcast to {}",
stringify!($impl_type)))
- ))?;
- return Ok(Box::new(*concrete) as Box<dyn $trait_name>);
- }
- )+
+ // Create helper functions for this trait with trait-specific names
+ $crate::paste::paste! {
+ #[allow(non_snake_case)]
+ mod [<__fory_trait_helpers_ $trait_name>] {
+ use super::*;
+
+ #[allow(dead_code)]
+ pub fn [<from_any_internal_ $trait_name>](
+ any_box: Box<dyn std::any::Any>,
+ _fory_type_id: u32,
+ ) -> Result<Box<dyn $trait_name>, $crate::error::Error> {
+ $(
+ if any_box.is::<$impl_type>() {
+ let concrete = any_box.downcast::<$impl_type>()
+ .map_err(|_| $crate::error::Error::Other(
+
$crate::error::AnyhowError::msg(format!("Failed to downcast to {}",
stringify!($impl_type)))
+ ))?;
+ return Ok(Box::new(*concrete) as Box<dyn
$trait_name>);
+ }
+ )+
-
Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
- format!("No matching type found for trait {}",
stringify!($trait_name))
- )))
+
Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("No matching type found for trait {}",
stringify!($trait_name))
+ )))
+ }
}
}
};
@@ -638,7 +644,7 @@ impl Serializer for Box<dyn Serializer> {
/// Helper macros for automatic conversions in derive code
/// These are used by fory-derive to generate transparent conversions
///
-/// Convert field of type Rc<dyn Trait> to wrapper for serialization
+/// Convert field of type `Rc<dyn Trait>` to wrapper for serialization
#[macro_export]
macro_rules! wrap_rc {
($field:expr, $trait_name:ident) => {
@@ -648,7 +654,7 @@ macro_rules! wrap_rc {
};
}
-/// Convert wrapper back to Rc<dyn Trait> for deserialization
+/// Convert wrapper back to `Rc<dyn Trait>` for deserialization
#[macro_export]
macro_rules! unwrap_rc {
($wrapper:expr, $trait_name:ident) => {
@@ -656,7 +662,7 @@ macro_rules! unwrap_rc {
};
}
-/// Convert Arc<dyn Trait> to wrapper for serialization
+/// Convert `Arc<dyn Trait>` to wrapper for serialization
#[macro_export]
macro_rules! wrap_arc {
($field:expr, $trait_name:ident) => {
@@ -666,7 +672,7 @@ macro_rules! wrap_arc {
};
}
-/// Convert Vec<Rc<dyn Trait>> to Vec<wrapper> for serialization
+/// Convert `Vec<Rc<dyn Trait>>` to `Vec<wrapper>` for serialization
#[macro_export]
macro_rules! wrap_vec_rc {
($vec:expr, $trait_name:ident) => {
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index 0bd037783..ae4a2d819 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -39,7 +39,7 @@ fn declare_var(fields: &[&Field]) -> Vec<TokenStream> {
let ty = &field.ty;
let var_name = create_private_field_name(field);
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn
+ TraitObjectField::BoxDyn(_)
| TraitObjectField::RcDyn(_)
| TraitObjectField::ArcDyn(_) => {
quote! {
@@ -63,7 +63,7 @@ fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
let name = &field.ident;
let var_name = create_private_field_name(field);
match classify_trait_object_field(&field.ty) {
- TraitObjectField::BoxDyn
+ TraitObjectField::BoxDyn(_)
| TraitObjectField::RcDyn(_)
| TraitObjectField::ArcDyn(_) => {
quote! {
@@ -85,7 +85,9 @@ fn gen_read_match_arm(field: &Field, private_ident: &Ident)
-> TokenStream {
let name_str = field.ident.as_ref().unwrap().to_string();
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn => {
+ TraitObjectField::BoxDyn(trait_name) => {
+ let from_any_fn = format_ident!("from_any_internal_{}",
trait_name);
+ let helper_mod = format_ident!("__fory_trait_helpers_{}",
trait_name);
quote! {
#name_str => {
let ref_flag = context.reader.read_i8();
@@ -104,7 +106,7 @@ fn gen_read_match_arm(field: &Field, private_ident: &Ident)
-> TokenStream {
let any_box = deserializer_fn(context, true, false)?;
let base_type_id = fory_type_id >> 8;
- #private_ident =
__fory_trait_helpers::from_any_internal(any_box, base_type_id)?;
+ #private_ident = #helper_mod::#from_any_fn(any_box,
base_type_id)?;
}
}
}
@@ -214,7 +216,7 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
.map(|(field, private_ident)| {
let ty = &field.ty;
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn
+ TraitObjectField::BoxDyn(_)
| TraitObjectField::RcDyn(_)
| TraitObjectField::ArcDyn(_) => {
quote! {
@@ -250,7 +252,7 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
let original_ident = &field.ident;
let ty = &field.ty;
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn
+ TraitObjectField::BoxDyn(_)
| TraitObjectField::RcDyn(_)
| TraitObjectField::ArcDyn(_) => {
quote! {
@@ -277,7 +279,9 @@ fn gen_read_compatible_match_arm(field: &Field, var_name:
&Ident) -> TokenStream
let field_name_str = field.ident.as_ref().unwrap().to_string();
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn => {
+ TraitObjectField::BoxDyn(trait_name) => {
+ let from_any_fn = format_ident!("from_any_internal_{}",
trait_name);
+ let helper_mod = format_ident!("__fory_trait_helpers_{}",
trait_name);
quote! {
if _field.field_name.as_str() == #field_name_str {
let ref_flag = context.reader.read_i8();
@@ -292,7 +296,7 @@ fn gen_read_compatible_match_arm(field: &Field, var_name:
&Ident) -> TokenStream
let deserializer_fn = harness.get_deserializer();
let any_box = deserializer_fn(context, true,
false).unwrap();
let base_type_id = fory_type_id >> 8;
- #var_name =
__fory_trait_helpers::from_any_internal(any_box, base_type_id).unwrap();
+ #var_name = #helper_mod::#from_any_fn(any_box,
base_type_id).unwrap();
}
}
}
diff --git a/rust/fory-derive/src/object/util.rs
b/rust/fory-derive/src/object/util.rs
index dfdc42431..4dde75605 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -73,7 +73,7 @@ pub(super) fn create_wrapper_types_arc(trait_name: &str) ->
WrapperTypes {
}
pub(super) enum TraitObjectField {
- BoxDyn,
+ BoxDyn(String),
RcDyn(String),
ArcDyn(String),
VecRc(String),
@@ -85,8 +85,8 @@ pub(super) enum TraitObjectField {
}
pub(super) fn classify_trait_object_field(ty: &Type) -> TraitObjectField {
- if is_box_dyn_trait(ty).is_some() {
- return TraitObjectField::BoxDyn;
+ if let Some((_, trait_name)) = is_box_dyn_trait(ty) {
+ return TraitObjectField::BoxDyn(trait_name);
}
if let Some((_, trait_name)) = is_rc_dyn_trait(ty) {
return TraitObjectField::RcDyn(trait_name);
diff --git a/rust/fory-derive/src/object/write.rs
b/rust/fory-derive/src/object/write.rs
index aab92a1f0..65fea320c 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -27,7 +27,7 @@ pub fn gen_reserved_space(fields: &[&Field]) -> TokenStream {
let reserved_size_expr: Vec<_> = fields.iter().map(|field| {
let ty = &field.ty;
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn => {
+ TraitObjectField::BoxDyn(_) => {
quote! {
fory_core::types::SIZE_OF_REF_AND_TYPE
}
@@ -100,7 +100,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let name_str = ident.as_ref().unwrap().to_string();
match classify_trait_object_field(ty) {
- TraitObjectField::BoxDyn => {
+ TraitObjectField::BoxDyn(_) => {
quote! {
#name_str => {
let any_ref = self.#ident.as_any();
diff --git a/rust/fory-derive/src/util.rs b/rust/fory-derive/src/util.rs
index 2cdecc274..66373dfc1 100644
--- a/rust/fory-derive/src/util.rs
+++ b/rust/fory-derive/src/util.rs
@@ -23,8 +23,8 @@ pub fn sorted_fields(fields: &Fields) -> Vec<&Field> {
fields
}
-/// Check if a type is `Box<dyn Trait>` and return the trait type if it is
-pub fn is_box_dyn_trait(ty: &Type) -> Option<&TypeTraitObject> {
+/// Check if a type is `Box<dyn Trait>` and return the trait type and trait
name if it is
+pub fn is_box_dyn_trait(ty: &Type) -> Option<(&TypeTraitObject, String)> {
if let Type::Path(TypePath { path, .. }) = ty {
if let Some(seg) = path.segments.last() {
if seg.ident == "Box" {
@@ -32,7 +32,15 @@ pub fn is_box_dyn_trait(ty: &Type) ->
Option<&TypeTraitObject> {
if let
Some(GenericArgument::Type(Type::TraitObject(trait_obj))) =
args.args.first()
{
- return Some(trait_obj);
+ // Extract trait name from the trait object
+ if let Some(syn::TypeParamBound::Trait(trait_bound)) =
+ trait_obj.bounds.first()
+ {
+ if let Some(segment) =
trait_bound.path.segments.last() {
+ let trait_name = segment.ident.to_string();
+ return Some((trait_obj, trait_name));
+ }
+ }
}
}
}
diff --git a/rust/tests/tests/test_trait_object.rs
b/rust/tests/tests/test_trait_object.rs
index bc1e5a299..bfea6c251 100644
--- a/rust/tests/tests/test_trait_object.rs
+++ b/rust/tests/tests/test_trait_object.rs
@@ -321,6 +321,73 @@ fn test_custom_trait_object_basic() {
assert_eq!(deserialized_cat.star_animal.speak(), "Meow!");
}
+trait Pet: Serializer {
+ fn pet_name(&self) -> &str;
+}
+
+impl Pet for Dog {
+ fn pet_name(&self) -> &str {
+ &self.name
+ }
+}
+
+impl Pet for Cat {
+ fn pet_name(&self) -> &str {
+ &self.name
+ }
+}
+
+register_trait_type!(Pet, Dog, Cat);
+
+#[derive(ForyObject)]
+struct PetOwner {
+ pets: Vec<Box<dyn Pet>>,
+ animals: Vec<Box<dyn Animal>>,
+}
+
+#[test]
+fn test_multiple_traits() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+ fory.register::<PetOwner>(9001);
+
+ let owner = PetOwner {
+ pets: vec![
+ Box::new(Dog {
+ name: "Rex".to_string(),
+ breed: "German Shepherd".to_string(),
+ }),
+ Box::new(Cat {
+ name: "Luna".to_string(),
+ color: "Black".to_string(),
+ }),
+ ],
+ animals: vec![
+ Box::new(Dog {
+ name: "Rex".to_string(),
+ breed: "German Shepherd".to_string(),
+ }),
+ Box::new(Cat {
+ name: "Luna".to_string(),
+ color: "Black".to_string(),
+ }),
+ ],
+ };
+
+ let serialized = fory.serialize(&owner);
+ let deserialized: PetOwner = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.pets.len(), 2);
+ assert_eq!(deserialized.pets[0].pet_name(), "Rex");
+ assert_eq!(deserialized.pets[1].pet_name(), "Luna");
+ assert_eq!(deserialized.animals.len(), 2);
+ assert_eq!(deserialized.animals[0].name(), "Rex");
+ assert_eq!(deserialized.animals[0].speak(), "Woof!");
+ assert_eq!(deserialized.animals[1].name(), "Luna");
+ assert_eq!(deserialized.animals[1].speak(), "Meow!");
+}
+
// Tests for direct Vec<Box<dyn CustomTrait>> and HashMap<String, Box<dyn
CustomTrait>>
// These should work automatically now with the enhanced register_trait_type!
macro
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]