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 0ec7261e8 refactor(rust): refactor sync send type (#3737)
0ec7261e8 is described below
commit 0ec7261e83833867f977b95da25c24d9c678be7e
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Jun 4 15:49:08 2026 +0800
refactor(rust): refactor sync send type (#3737)
## Why?
## What does this PR do?
## Related issues
#3732
#3736
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
AGENTS.md | 4 +-
docs/guide/rust/custom-serializers.md | 39 ++-
docs/guide/rust/native-serialization.md | 5 +-
docs/guide/rust/polymorphism.md | 46 ++-
docs/guide/rust/schema-evolution.md | 18 +-
rust/README.md | 12 +
rust/fory-core/src/error.rs | 12 +
rust/fory-core/src/fory.rs | 10 -
rust/fory-core/src/resolver/type_resolver.rs | 123 +++-----
rust/fory-core/src/serializer/any.rs | 176 +++++++----
rust/fory-core/src/serializer/arc.rs | 11 +-
rust/fory-core/src/serializer/bool.rs | 11 +
rust/fory-core/src/serializer/core.rs | 197 +-----------
rust/fory-core/src/serializer/datetime.rs | 66 ++++
rust/fory-core/src/serializer/decimal.rs | 11 +
rust/fory-core/src/serializer/mod.rs | 7 +-
rust/fory-core/src/serializer/number.rs | 33 ++
rust/fory-core/src/serializer/string.rs | 11 +
rust/fory-core/src/serializer/tuple.rs | 11 +
rust/fory-core/src/serializer/unknown_case.rs | 13 +-
rust/fory-core/src/serializer/unsigned_number.rs | 22 ++
rust/fory-core/src/serializer/util.rs | 12 +
rust/fory-core/src/types/unknown_case.rs | 2 +-
rust/fory-derive/src/lib.rs | 7 +
rust/fory-derive/src/object/field_codec.rs | 2 +-
rust/fory-derive/src/object/serializer.rs | 48 ++-
rust/fory-derive/src/object/util.rs | 12 +-
rust/fory/src/lib.rs | 31 +-
rust/tests/tests/test_any.rs | 342 ++++++++++++++++++---
rust/tests/tests/test_collection.rs | 9 +-
rust/tests/tests/test_enum.rs | 2 +-
rust/tests/tests/test_lifecycle_guard.rs | 13 -
rust/tests/tests/test_send_sync.rs | 365 +++++++++++++++++++++++
rust/tests/tests/test_trait_object.rs | 87 ++++--
rust/tests/tests/test_unsigned.rs | 101 +++++--
35 files changed, 1328 insertions(+), 543 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index d6d7d31af..aca7ebaec 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -68,7 +68,9 @@ This is the entry point for AI guidance in Apache Fory. Read
this file first, th
- User guide docs must explain user-visible behavior, commands, and examples.
Do not add implementation details, internal ownership rationale, build flags,
or type-id-space caveats unless they directly clarify a confusion users can
- act on.
+ act on. Translate internal owner-model details into concrete user actions,
and
+ avoid phrases such as "serializer-owned capability" or "registration alone
+ does not..." in user-facing docs.
- Add comments only when behavior is hard to understand or an algorithm is
non-obvious.
- Do not remove existing code comments unless they are stale, misleading,
redundant, or no longer necessary after the change.
- Only add tests that verify internal behaviors or fix specific bugs; do not
create unnecessary tests unless requested.
diff --git a/docs/guide/rust/custom-serializers.md
b/docs/guide/rust/custom-serializers.md
index c3c5e8f90..e5c273e0a 100644
--- a/docs/guide/rust/custom-serializers.md
+++ b/docs/guide/rust/custom-serializers.md
@@ -31,7 +31,7 @@ For types that don't support `#[derive(ForyStruct)]`,
implement the `Serializer`
## Implementing the Serializer Trait
```rust
-use fory::{Fory, ReadContext, WriteContext, Serializer, ForyDefault, Error};
+use fory::{Error, Fory, ForyDefault, ReadContext, Serializer, TypeResolver,
WriteContext};
use std::any::Any;
#[derive(Debug, PartialEq, Default)]
@@ -41,20 +41,21 @@ struct CustomType {
}
impl Serializer for CustomType {
- fn fory_write_data(&self, context: &mut WriteContext, is_field: bool) {
+ fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error>
{
context.writer.write_i32(self.value);
- context.writer.write_varuint32(self.name.len() as u32);
+ context.writer.write_var_u32(self.name.len() as u32);
context.writer.write_utf8_string(&self.name);
+ Ok(())
}
- fn fory_read_data(context: &mut ReadContext, is_field: bool) ->
Result<Self, Error> {
- let value = context.reader.read_i32();
- let len = context.reader.read_varuint32() as usize;
- let name = context.reader.read_utf8_string(len);
+ fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
+ let value = context.reader.read_i32()?;
+ let len = context.reader.read_var_u32()? as usize;
+ let name = context.reader.read_utf8_string(len)?;
Ok(Self { value, name })
}
- fn fory_type_id_dyn(&self, type_resolver: &TypeResolver) -> u32 {
+ fn fory_type_id_dyn(&self, type_resolver: &TypeResolver) ->
Result<fory::TypeId, Error> {
Self::fory_get_type_id(type_resolver)
}
@@ -76,6 +77,28 @@ impl ForyDefault for CustomType {
>
> **Tip**: If your type supports `#[derive(ForyStruct)]`, you can use
> `#[fory(generate_default)]` to automatically generate both `ForyDefault` and
> `Default` implementations.
+## Manual Serializers and Arc Any
+
+If a manually registered serializer needs its type to round-trip behind
+`Arc<dyn Any + Send + Sync>` or preserve `UnknownCase` payloads, implement the
+send-sync Any reader and return the concrete value as a boxed `Any` value:
+
+```rust
+impl Serializer for CustomType {
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn Any + Send + Sync>, Error> {
+ Ok(Box::new(Self::fory_read_data(context)?))
+ }
+
+ // Implement the ordinary Serializer methods as shown above.
+ // ...
+}
+```
+
+Do not override this method for values that contain fields whose types are not
+`Send + Sync`, such as `Rc<T>`, `RcWeak<T>`, `RefCell<T>`, or `Cell<T>`.
+
## Registering Custom Serializers
```rust
diff --git a/docs/guide/rust/native-serialization.md
b/docs/guide/rust/native-serialization.md
index e2bf6e74f..133b022b9 100644
--- a/docs/guide/rust/native-serialization.md
+++ b/docs/guide/rust/native-serialization.md
@@ -100,7 +100,10 @@ Native serialization owns the Rust-specific object surface:
- `Box<T>`, `Rc<T>`, `Arc<T>`, `RcWeak<T>`, and `ArcWeak<T>`.
- `RefCell<T>` and `Mutex<T>`.
- Trait objects such as `Box<dyn Trait>`, `Rc<dyn Trait>`, and `Arc<dyn
Trait>`.
-- Runtime type dispatch with `Rc<dyn Any>` and `Arc<dyn Any + Send + Sync>`.
+- Runtime type dispatch with `Box<dyn Any>`, `Rc<dyn Any>`, and
+ `Arc<dyn Any + Send + Sync>` for registered non-container payloads. Wrap
+ containers in registered structs, enums, or unions before using them behind
+ erased `Any` carriers.
- Date and time carriers, including optional `chrono` support.
Use [Basic Serialization](basic-serialization.md),
[References](references.md), and
diff --git a/docs/guide/rust/polymorphism.md b/docs/guide/rust/polymorphism.md
index fa87735ed..de8547c1b 100644
--- a/docs/guide/rust/polymorphism.md
+++ b/docs/guide/rust/polymorphism.md
@@ -85,12 +85,12 @@ assert_eq!(decoded.star_animal.speak(), "Woof!");
## Serializing dyn Any Trait Objects
-Apache Fory™ supports serializing `Rc<dyn Any>` and
+Apache Fory™ supports serializing `Box<dyn Any>`, `Rc<dyn Any>`, and
`Arc<dyn Any + Send + Sync>` for runtime type dispatch:
**Key points:**
-- Works with any type that implements `Serializer`
+- Works with registered concrete non-container types that implement
`Serializer`
- Requires downcasting after deserialization to access the concrete type
- Type information is preserved during serialization
- Useful for plugin systems and dynamic type handling
@@ -132,6 +132,46 @@ let unwrapped = decoded.downcast_ref::<Dog>().unwrap();
assert_eq!(unwrapped.name, "Buddy");
```
+`Box<dyn Any>`, `Rc<dyn Any>`, and `Arc<dyn Any + Send + Sync>` are supported
+erased `Any` carriers for registered concrete non-container payloads.
+Use `Arc<dyn Any + Send + Sync>` when the erased payload must be shareable
+across threads; the concrete payload type must also satisfy `Send + Sync`.
+Registered structs, enums, and unions that satisfy those bounds can be used as
+the erased payload.
+
+The unsupported case is a generic container used directly as the top-level
+erased payload. This applies to all erased `Any` carriers: `Box<dyn Any>`,
+`Rc<dyn Any>`, and `Arc<dyn Any + Send + Sync>`. Unsupported direct payloads
+include list-, map-, and set-like containers such as `Vec<T>`, `Vec<u8>`,
+`HashMap<K, V>`, `HashSet<T>`, and `LinkedList<T>`.
+
+If you need to put a container in an erased `Any` payload, wrap it in a
+registered struct, enum, or union and use that wrapper as the erased payload:
+
+```rust
+use fory::{Fory, ForyStruct};
+use std::any::Any;
+use std::sync::Arc;
+
+#[derive(ForyStruct)]
+struct IntList {
+ values: Vec<i32>,
+}
+
+let mut fory = Fory::builder().xlang(false).build();
+fory.register::<IntList>(100)?;
+
+let value: Arc<dyn Any + Send + Sync> = Arc::new(IntList {
+ values: vec![1, 2, 3],
+});
+let bytes = fory.serialize(&value)?;
+let decoded: Arc<dyn Any + Send + Sync> = fory.deserialize(&bytes)?;
+```
+
+The wrapper makes the erased payload a concrete registered type while the
+container remains a normal typed field. The same wrapper model is the supported
+path for `Box<dyn Any>` and `Rc<dyn Any>`.
+
## Rc/Arc-Based Trait Objects in Structs
For fields with `Rc<dyn Trait>` or `Arc<dyn Trait>`, Fory automatically
handles the conversion:
@@ -180,7 +220,7 @@ assert_eq!(decoded.animals_arc[0].speak(), "Woof!");
Due to Rust's orphan rule, `Rc<dyn Trait>` and `Arc<dyn Trait>` cannot
implement `Serializer` directly. For standalone serialization (not inside
struct fields), the `register_trait_type!` macro generates wrapper types.
-**Note:** If you don't want to use wrapper types, you can serialize as `Rc<dyn
Any>` or `Arc<dyn Any + Send + Sync>` instead (see the dyn Any section above).
+**Note:** If you don't want to use wrapper types for concrete non-container
payloads, you can serialize as `Box<dyn Any>`, `Rc<dyn Any>`, or `Arc<dyn Any +
Send + Sync>` instead (see the dyn Any section above).
The `register_trait_type!` macro generates `AnimalRc` and `AnimalArc` wrapper
types:
diff --git a/docs/guide/rust/schema-evolution.md
b/docs/guide/rust/schema-evolution.md
index 2766641ca..14c536afc 100644
--- a/docs/guide/rust/schema-evolution.md
+++ b/docs/guide/rust/schema-evolution.md
@@ -129,14 +129,16 @@ let decoded: Value = fory.deserialize(&bytes)?;
assert_eq!(value, decoded);
```
-For typed ADT unions whose schema cases are unit or single-payload variants,
-`#[fory(unknown)] Unknown(::fory::UnknownCase)` is only the runtime
-forward-compatibility carrier. It cannot be the default variant, and the union
-must include at least one real schema case. The marker only selects the carrier
-and does not add an entry to the schema case table; schema cases use
-non-negative IDs. `UnknownCase` stores its payload as
-`Arc<dyn Any + Send + Sync>`, so locally registered future payload types must
be
-thread-safe to be preserved as unknown cases.
+For typed ADT unions whose cases are unit or single-payload variants, add an
+`#[fory(unknown)] Unknown(::fory::UnknownCase)` variant when you need to
+preserve future payload variants. Do not make the unknown variant the default;
+keep a real schema case marked `#[fory(default)]`. Register future payload
types
+locally before deserializing unknown cases you need to preserve.
+
+`UnknownCase` stores its payload as `Arc<dyn Any + Send + Sync>`, so preserved
+payload types must satisfy `Send + Sync`. Direct generic containers are not
+supported as erased `Any` payloads; wrap the container in a registered derived
+type if an unknown case needs to preserve it.
### Enum Schema Evolution
diff --git a/rust/README.md b/rust/README.md
index dd9ea0708..8f238c0ce 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -251,6 +251,18 @@ The examples in this section use native mode because Rust
trait objects and `dyn
- `Box<dyn Any>`/`Rc<dyn Any>`/`Arc<dyn Any + Send + Sync>` - Any trait type
objects
- `Vec<Box<dyn Trait>>`, `HashMap<K, Box<dyn Trait>>` - Collections of trait
objects
+`Box<dyn Any>`, `Rc<dyn Any>`, and `Arc<dyn Any + Send + Sync>` are supported
+erased `Any` carriers for registered concrete non-container payloads.
+Use `Arc<dyn Any + Send + Sync>` when the erased payload must be shareable
+across threads; the concrete payload type must also satisfy `Send + Sync`.
+Registered structs, enums, and unions that satisfy those bounds can be used as
+the erased payload.
+Generic containers such as `Vec<T>`, `HashMap<K, V>`, `HashSet<T>`, and
+`LinkedList<T>` are not supported directly as top-level erased `Any` payloads
+behind any of those carriers. This also includes primitive vector encodings
such
+as `Vec<u8>`. Wrap the container in a registered derived type when it needs to
+travel behind an erased `Any` carrier.
+
**Basic Trait Object Serialization Example:**
```rust
diff --git a/rust/fory-core/src/error.rs b/rust/fory-core/src/error.rs
index c12a9f751..9311a8a4a 100644
--- a/rust/fory-core/src/error.rs
+++ b/rust/fory-core/src/error.rs
@@ -552,6 +552,18 @@ impl Error {
}
}
+#[cold]
+#[inline(never)]
+pub(crate) fn unsupported_send_sync_type<T>() -> Error
+where
+ T: ?Sized,
+{
+ Error::type_error(format!(
+ "{} cannot be represented as Arc<dyn Any + Send + Sync>",
+ std::any::type_name::<T>()
+ ))
+}
+
/// Ensures a condition is true; otherwise returns an [`enum@Error`].
///
/// # Examples
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index 2b9b7f621..4a491616b 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -954,16 +954,6 @@ impl Fory {
.register_serializer_by_name::<T>(namespace, type_name)
}
- /// Registers a generic trait object type for serialization.
- /// This method should be used to register collection types such as
`Vec<T>`, `HashMap<K, V>`, etc.
- /// Don't register concrete struct types with this method. Use
`register()` instead.
- pub fn register_generic_trait<T: 'static + Serializer + ForyDefault>(
- &mut self,
- ) -> Result<(), Error> {
- self.check_registration_allowed()?;
- self.type_resolver.register_generic_trait::<T>()
- }
-
/// Writes the serialization header to the writer.
#[inline(always)]
pub fn write_head<T: Serializer>(&self, writer: &mut Writer) {
diff --git a/rust/fory-core/src/resolver/type_resolver.rs
b/rust/fory-core/src/resolver/type_resolver.rs
index bb9ae8e53..3a5c9968b 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -28,7 +28,6 @@ use crate::types::{Date, Duration, Timestamp};
use crate::TypeId;
#[cfg(feature = "chrono")]
use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime};
-use std::collections::{HashSet, LinkedList};
use std::rc::Rc;
use std::vec;
@@ -63,9 +62,9 @@ type ReadFn =
type WriteDataFn = fn(&dyn Any, &mut WriteContext, has_generics: bool) ->
Result<(), Error>;
type ReadDataFn = fn(&mut ReadContext) -> Result<Box<dyn Any>, Error>;
-type ReadDataSendSyncFn = fn(&mut ReadContext) -> Result<Box<dyn Any + Send +
Sync>, Error>;
+type ReadDataAsSendSyncAnyFn = fn(&mut ReadContext) -> Result<Box<dyn Any +
Send + Sync>, Error>;
type ReadCompatibleFn = fn(&mut ReadContext, Rc<TypeInfo>) -> Result<Box<dyn
Any>, Error>;
-type ReadCompatibleSendSyncFn =
+type ReadCompatibleAsSendSyncAnyFn =
fn(&mut ReadContext, Rc<TypeInfo>) -> Result<Box<dyn Any + Send + Sync>,
Error>;
type ToSerializerFn = fn(Box<dyn Any>) -> Result<Box<dyn Serializer>, Error>;
type BuildTypeInfosFn = fn(&TypeResolver) -> Result<Vec<(std::any::TypeId,
TypeInfo)>, Error>;
@@ -90,10 +89,9 @@ pub struct Harness {
read_fn: ReadFn,
write_data_fn: WriteDataFn,
read_data_fn: ReadDataFn,
- read_data_send_sync_fn: ReadDataSendSyncFn,
+ read_data_as_send_sync_any_fn: ReadDataAsSendSyncAnyFn,
read_compatible_fn: Option<ReadCompatibleFn>,
- read_compatible_send_sync_fn: Option<ReadCompatibleSendSyncFn>,
- threadsafe: bool,
+ read_compatible_as_send_sync_any_fn: Option<ReadCompatibleAsSendSyncAnyFn>,
to_serializer: ToSerializerFn,
build_type_infos: BuildTypeInfosFn,
}
@@ -105,10 +103,9 @@ impl Harness {
read_fn: stub_read_fn,
write_data_fn: stub_write_data_fn,
read_data_fn: stub_read_data_fn,
- read_data_send_sync_fn: stub_read_data_send_sync_fn,
+ read_data_as_send_sync_any_fn: stub_read_data_as_send_sync_any_fn,
read_compatible_fn: None,
- read_compatible_send_sync_fn: None,
- threadsafe: false,
+ read_compatible_as_send_sync_any_fn: None,
to_serializer: stub_to_serializer_fn,
build_type_infos: stub_build_type_infos,
}
@@ -166,23 +163,17 @@ impl Harness {
/// This path never upgrades an ordinary `Box<dyn Any>`; it delegates to
/// type-owned readers that construct the send-sync trait object directly.
#[inline(always)]
- pub fn read_polymorphic_data_send_sync(
+ pub fn read_polymorphic_data_as_send_sync_any(
&self,
context: &mut ReadContext,
typeinfo: &Rc<TypeInfo>,
) -> Result<Box<dyn Any + Send + Sync>, Error> {
- if !self.threadsafe {
- return Err(Error::type_error(format!(
- "{}::{} cannot be represented as Arc<dyn Any + Send + Sync>",
- typeinfo.namespace.original, typeinfo.type_name.original
- )));
- }
if context.is_compatible() {
- if let Some(read_compatible_fn) =
self.read_compatible_send_sync_fn {
+ if let Some(read_compatible_fn) =
self.read_compatible_as_send_sync_any_fn {
return read_compatible_fn(context, typeinfo.clone());
}
}
- (self.read_data_send_sync_fn)(context)
+ (self.read_data_as_send_sync_any_fn)(context)
}
}
@@ -331,10 +322,9 @@ impl TypeInfo {
read_fn: stub_read_fn,
write_data_fn: stub_write_data_fn,
read_data_fn: stub_read_data_fn,
- read_data_send_sync_fn: stub_read_data_send_sync_fn,
+ read_data_as_send_sync_any_fn:
stub_read_data_as_send_sync_any_fn,
read_compatible_fn: None,
- read_compatible_send_sync_fn: None,
- threadsafe: false,
+ read_compatible_as_send_sync_any_fn: None,
to_serializer: stub_to_serializer_fn,
build_type_infos: stub_build_type_infos,
}
@@ -384,7 +374,9 @@ fn stub_read_data_fn(_: &mut ReadContext) -> Result<Box<dyn
Any>, Error> {
))
}
-fn stub_read_data_send_sync_fn(_: &mut ReadContext) -> Result<Box<dyn Any +
Send + Sync>, Error> {
+fn stub_read_data_as_send_sync_any_fn(
+ _: &mut ReadContext,
+) -> Result<Box<dyn Any + Send + Sync>, Error> {
Err(Error::type_error(
"Cannot deserialize unknown remote type as Arc<dyn Any + Send + Sync>
- type not registered locally",
))
@@ -826,15 +818,6 @@ impl TypeResolver {
self.register_internal_serializer::<Vec<u128>>(TypeId::U128_ARRAY)?;
self.register_internal_serializer::<Vec<isize>>(TypeId::ISIZE_ARRAY)?;
self.register_internal_serializer::<Vec<i128>>(TypeId::INT128_ARRAY)?;
- self.register_generic_trait::<Vec<String>>()?;
- self.register_generic_trait::<LinkedList<i32>>()?;
- self.register_generic_trait::<LinkedList<String>>()?;
- self.register_generic_trait::<HashSet<String>>()?;
- self.register_generic_trait::<HashSet<i32>>()?;
- self.register_generic_trait::<HashSet<i64>>()?;
- self.register_generic_trait::<HashMap<String, String>>()?;
- self.register_generic_trait::<HashMap<String, i32>>()?;
- self.register_generic_trait::<HashMap<String, i64>>()?;
Ok(())
}
@@ -914,6 +897,13 @@ impl TypeResolver {
} else {
id
};
+ let supports_compatible_read = matches!(
+ actual_type_id,
+ x if x == TypeId::STRUCT as u32
+ || x == TypeId::COMPATIBLE_STRUCT as u32
+ || x == TypeId::NAMED_STRUCT as u32
+ || x == TypeId::NAMED_COMPATIBLE_STRUCT as u32
+ );
fn write<T2: 'static + Serializer>(
this: &dyn Any,
@@ -966,18 +956,10 @@ impl TypeResolver {
}
}
- fn read_data_send_sync<T2: 'static + Serializer + ForyDefault>(
+ fn read_data_as_send_sync_any<T2: 'static + Serializer + ForyDefault>(
context: &mut ReadContext,
) -> Result<Box<dyn Any + Send + Sync>, Error> {
- if T2::fory_is_threadsafe_type() {
- T2::fory_read_data_send_sync(context)
- } else if crate::serializer::is_known_threadsafe_static_type_id(
- T2::fory_static_type_id(),
- ) {
- crate::serializer::read_known_threadsafe_data::<T2>(context)
- } else {
- Err(crate::serializer::unsupported_threadsafe_type::<T2>())
- }
+ T2::fory_read_data_as_send_sync_any(context)
}
fn to_serializer<T2: 'static + Serializer>(
@@ -1002,11 +984,11 @@ impl TypeResolver {
Ok(Box::new(T2::fory_read_compatible(context, type_info)?))
}
- fn read_compatible_send_sync<T2: 'static + StructSerializer +
ForyDefault>(
+ fn read_compatible_as_send_sync_any<T2: 'static + StructSerializer +
ForyDefault>(
context: &mut ReadContext,
type_info: Rc<TypeInfo>,
) -> Result<Box<dyn Any + Send + Sync>, Error> {
- T2::fory_read_compatible_send_sync(context, type_info)
+ T2::fory_read_compatible_as_send_sync_any(context, type_info)
}
let harness = Harness {
@@ -1014,11 +996,17 @@ impl TypeResolver {
read_fn: read::<T>,
write_data_fn: write_data::<T>,
read_data_fn: read_data::<T>,
- read_data_send_sync_fn: read_data_send_sync::<T>,
- read_compatible_fn: Some(read_compatible::<T>),
- read_compatible_send_sync_fn: Some(read_compatible_send_sync::<T>),
- threadsafe: T::fory_is_threadsafe_type()
- ||
crate::serializer::is_known_threadsafe_static_type_id(T::fory_static_type_id()),
+ read_data_as_send_sync_any_fn: read_data_as_send_sync_any::<T>,
+ read_compatible_fn: if supports_compatible_read {
+ Some(read_compatible::<T>)
+ } else {
+ None
+ },
+ read_compatible_as_send_sync_any_fn: if supports_compatible_read {
+ Some(read_compatible_as_send_sync_any::<T>)
+ } else {
+ None
+ },
to_serializer: to_serializer::<T>,
build_type_infos: build_type_infos::<T>,
};
@@ -1212,18 +1200,10 @@ impl TypeResolver {
}
}
- fn read_data_send_sync<T2: 'static + Serializer + ForyDefault>(
+ fn read_data_as_send_sync_any<T2: 'static + Serializer + ForyDefault>(
context: &mut ReadContext,
) -> Result<Box<dyn Any + Send + Sync>, Error> {
- if T2::fory_is_threadsafe_type() {
- T2::fory_read_data_send_sync(context)
- } else if crate::serializer::is_known_threadsafe_static_type_id(
- T2::fory_static_type_id(),
- ) {
- crate::serializer::read_known_threadsafe_data::<T2>(context)
- } else {
- Err(crate::serializer::unsupported_threadsafe_type::<T2>())
- }
+ T2::fory_read_data_as_send_sync_any(context)
}
fn to_serializer<T2: 'static + Serializer>(
@@ -1256,11 +1236,9 @@ impl TypeResolver {
read_fn: read::<T>,
write_data_fn: write_data::<T>,
read_data_fn: read_data::<T>,
- read_data_send_sync_fn: read_data_send_sync::<T>,
+ read_data_as_send_sync_any_fn: read_data_as_send_sync_any::<T>,
read_compatible_fn: None,
- read_compatible_send_sync_fn: None,
- threadsafe: T::fory_is_threadsafe_type()
- ||
crate::serializer::is_known_threadsafe_static_type_id(T::fory_static_type_id()),
+ read_compatible_as_send_sync_any_fn: None,
to_serializer: to_serializer::<T>,
build_type_infos: build_type_infos::<T>,
};
@@ -1319,27 +1297,6 @@ impl TypeResolver {
Ok(())
}
- /// Register a generic trait type like List, Map, Set
- pub fn register_generic_trait<T: 'static + Serializer + ForyDefault>(
- &mut self,
- ) -> Result<(), Error> {
- let rs_type_id = std::any::TypeId::of::<T>();
- if self.type_info_map.contains_key(&rs_type_id) {
- return Err(Error::type_error(format!(
- "Type:{:?} already registered",
- rs_type_id
- )));
- }
- let type_id = T::fory_static_type_id();
- if type_id != TypeId::LIST && type_id != TypeId::MAP && type_id !=
TypeId::SET {
- return Err(Error::not_allowed(format!(
- "register_generic_trait can only be used for generic trait
types: List, Map, Set, but got type {}",
- type_id as u32
- )));
- }
- self.register_internal_serializer::<T>(type_id)
- }
-
pub(crate) fn set_compatible(&mut self, compatible: bool) {
self.compatible = compatible;
}
diff --git a/rust/fory-core/src/serializer/any.rs
b/rust/fory-core/src/serializer/any.rs
index e4e9067be..e63bf698c 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -37,24 +37,91 @@ fn resolve_registered_type_id(
.ok_or_else(|| Error::type_error("Type not registered"))
}
-/// Check if the type info represents a generic container type (LIST, SET,
MAP).
-/// These types cannot be deserialized polymorphically via `Box<dyn Any>`
because
-/// different generic instantiations (e.g., `Vec<A>`, `Vec<B>`) share the same
type ID.
#[inline]
-pub(crate) fn check_generic_container_type(type_info: &TypeInfo) -> Result<(),
Error> {
- let type_id = type_info.get_type_id();
- if type_id == TypeId::LIST || type_id == TypeId::SET || type_id ==
TypeId::MAP {
- return Err(Error::type_error(
- "Cannot deserialize generic container types (Vec, HashSet,
HashMap) polymorphically \
- via Box<dyn Any>, Rc<dyn Any>, or Arc<dyn Any + Send + Sync>. The
serialization protocol does not preserve the element type \
- information needed to distinguish between different generic
instantiations \
- (e.g., Vec<StructA> vs Vec<StructB>). Consider wrapping the
container in a \
- named struct type instead.",
- ));
+fn is_erased_any_container_type(type_id: TypeId) -> bool {
+ matches!(
+ type_id,
+ TypeId::LIST
+ | TypeId::SET
+ | TypeId::MAP
+ | TypeId::BINARY
+ | TypeId::ARRAY
+ | TypeId::BOOL_ARRAY
+ | TypeId::INT8_ARRAY
+ | TypeId::INT16_ARRAY
+ | TypeId::INT32_ARRAY
+ | TypeId::INT64_ARRAY
+ | TypeId::UINT8_ARRAY
+ | TypeId::UINT16_ARRAY
+ | TypeId::UINT32_ARRAY
+ | TypeId::UINT64_ARRAY
+ | TypeId::FLOAT8_ARRAY
+ | TypeId::FLOAT16_ARRAY
+ | TypeId::BFLOAT16_ARRAY
+ | TypeId::FLOAT32_ARRAY
+ | TypeId::FLOAT64_ARRAY
+ | TypeId::U128_ARRAY
+ | TypeId::INT128_ARRAY
+ | TypeId::USIZE_ARRAY
+ | TypeId::ISIZE_ARRAY
+ )
+}
+
+#[cold]
+#[inline(never)]
+fn unsupported_erased_any_container() -> Error {
+ Error::type_error(
+ "List-, map-, and set-like containers cannot be used as top-level
erased Any \
+ payloads via Box<dyn Any>, Rc<dyn Any>, or Arc<dyn Any + Send + Sync>.
This \
+ includes Vec<T>, byte and numeric vectors, LinkedList<T>, HashSet<T>,
and \
+ HashMap<K, V>. Wrap the container in a registered struct, enum, or
union and \
+ use that wrapper as the erased payload.",
+ )
+}
+
+#[cold]
+#[inline(never)]
+fn erased_any_type_info_error(err: Error) -> Error {
+ Error::type_error(format!(
+ "{err}. Erased Any payloads require a registered concrete
non-container type. \
+ Top-level list-, map-, and set-like containers such as Vec<T>,
LinkedList<T>, \
+ HashSet<T>, and HashMap<K, V> are unsupported; wrap the container in a
\
+ registered struct, enum, or union."
+ ))
+}
+
+#[inline]
+pub(crate) fn check_erased_any_payload_type(type_info: &TypeInfo) ->
Result<(), Error> {
+ if is_erased_any_container_type(type_info.get_type_id()) {
+ return Err(unsupported_erased_any_container());
}
Ok(())
}
+#[inline]
+fn get_erased_any_type_info(
+ context: &WriteContext,
+ concrete_type_id: &std::any::TypeId,
+) -> Result<Rc<TypeInfo>, Error> {
+ let type_info = context
+ .get_type_info(concrete_type_id)
+ .map_err(erased_any_type_info_error)?;
+ check_erased_any_payload_type(&type_info)?;
+ Ok(type_info)
+}
+
+#[inline]
+fn write_erased_any_type_info(
+ context: &mut WriteContext,
+ concrete_type_id: std::any::TypeId,
+) -> Result<Rc<TypeInfo>, Error> {
+ let type_info = context
+ .write_any_type_info(TypeId::UNKNOWN as u32, concrete_type_id)
+ .map_err(erased_any_type_info_error)?;
+ check_erased_any_payload_type(&type_info)?;
+ Ok(type_info)
+}
+
/// Helper function to deserialize to `Box<dyn Any>`
pub fn deserialize_any_box(context: &mut ReadContext) -> Result<Box<dyn Any>,
Error> {
context.inc_depth()?;
@@ -63,8 +130,7 @@ pub fn deserialize_any_box(context: &mut ReadContext) ->
Result<Box<dyn Any>, Er
return Err(Error::invalid_ref("Expected NotNullValue for Box<dyn
Any>"));
}
let typeinfo = context.read_any_type_info()?;
- // Check for generic container types which cannot be deserialized
polymorphically
- check_generic_container_type(&typeinfo)?;
+ check_erased_any_payload_type(&typeinfo)?;
let result = typeinfo
.get_harness()
.read_polymorphic_data(context, &typeinfo);
@@ -101,7 +167,7 @@ impl Serializer for Box<dyn Any> {
has_generics: bool,
) -> Result<(), Error> {
let concrete_type_id = (**self).type_id();
- let typeinfo = context.get_type_info(&concrete_type_id)?;
+ let typeinfo = get_erased_any_type_info(context, &concrete_type_id)?;
let serializer_fn = typeinfo.get_harness().get_write_data_fn();
serializer_fn(&**self, context, has_generics)
}
@@ -131,7 +197,7 @@ impl Serializer for Box<dyn Any> {
fn fory_read_data(_: &mut ReadContext) -> Result<Self, Error> {
Err(Error::not_allowed(
- "fory_read_data should not be called directly on polymorphic
Rc<dyn Any> trait object",
+ "fory_read_data should not be called directly on polymorphic
Box<dyn Any> trait object",
))
}
@@ -188,9 +254,9 @@ pub fn write_box_any(
}
let concrete_type_id = value.type_id();
let typeinfo = if write_type_info {
- context.write_any_type_info(TypeId::UNKNOWN as u32, concrete_type_id)?
+ write_erased_any_type_info(context, concrete_type_id)?
} else {
- context.get_type_info(&concrete_type_id)?
+ get_erased_any_type_info(context, &concrete_type_id)?
};
let serializer_fn = typeinfo.get_harness().get_write_data_fn();
serializer_fn(value, context, has_generics)
@@ -222,8 +288,7 @@ pub fn read_box_any(
);
context.read_any_type_info()?
};
- // Check for generic container types which cannot be deserialized
polymorphically
- check_generic_container_type(&typeinfo)?;
+ check_erased_any_payload_type(&typeinfo)?;
let result = typeinfo
.get_harness()
.read_polymorphic_data(context, &typeinfo);
@@ -251,16 +316,12 @@ impl Serializer for Rc<dyn Any> {
.try_write_rc_ref(&mut context.writer, self)
{
let concrete_type_id: std::any::TypeId = (**self).type_id();
- let write_data_fn = if write_type_info {
- let typeinfo =
- context.write_any_type_info(TypeId::UNKNOWN as u32,
concrete_type_id)?;
- typeinfo.get_harness().get_write_data_fn()
+ let typeinfo = if write_type_info {
+ write_erased_any_type_info(context, concrete_type_id)?
} else {
- context
- .get_type_info(&concrete_type_id)?
- .get_harness()
- .get_write_data_fn()
+ get_erased_any_type_info(context, &concrete_type_id)?
};
+ let write_data_fn = typeinfo.get_harness().get_write_data_fn();
write_data_fn(&**self, context, has_generics)?;
}
Ok(())
@@ -298,10 +359,9 @@ impl Serializer for Rc<dyn Any> {
}
fn fory_read_data(_: &mut ReadContext) -> Result<Self, Error> {
- Err(Error::not_allowed(format!(
- "fory_read_data should not be called directly on polymorphic
Rc<dyn {}> trait object",
- stringify!($trait_name)
- )))
+ Err(Error::not_allowed(
+ "fory_read_data should not be called directly on polymorphic
Rc<dyn Any> trait object",
+ ))
}
fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
@@ -321,14 +381,6 @@ impl Serializer for Rc<dyn Any> {
fn fory_is_shared_ref() -> bool {
true
}
-
- fn fory_is_threadsafe_type() -> bool
- where
- Self: Sized,
- {
- true
- }
-
fn fory_is_polymorphic() -> bool {
true
}
@@ -381,8 +433,7 @@ pub fn read_rc_any(
} else {
type_info.ok_or_else(|| Error::type_error("No type info found
for read"))?
};
- // Check for generic container types which cannot be deserialized
polymorphically
- check_generic_container_type(&typeinfo)?;
+ check_erased_any_payload_type(&typeinfo)?;
let boxed = typeinfo
.get_harness()
.read_polymorphic_data(context, &typeinfo)?;
@@ -390,20 +441,20 @@ pub fn read_rc_any(
Ok(Rc::<dyn Any>::from(boxed))
}
RefFlag::RefValue => {
+ let ref_id = context.ref_reader.reserve_ref_id();
context.inc_depth()?;
let typeinfo = if read_type_info {
context.read_any_type_info()?
} else {
type_info.ok_or_else(|| Error::type_error("No type info found
for read"))?
};
- // Check for generic container types which cannot be deserialized
polymorphically
- check_generic_container_type(&typeinfo)?;
+ check_erased_any_payload_type(&typeinfo)?;
let boxed = typeinfo
.get_harness()
.read_polymorphic_data(context, &typeinfo)?;
context.dec_depth();
let rc: Rc<dyn Any> = Rc::from(boxed);
- context.ref_reader.store_rc_ref(rc.clone());
+ context.ref_reader.store_rc_ref_at(ref_id, rc.clone());
Ok(rc)
}
}
@@ -430,18 +481,13 @@ impl Serializer for Arc<dyn Any + Send + Sync> {
{
let value: &dyn Any = self.as_ref();
let concrete_type_id: std::any::TypeId = value.type_id();
- if write_type_info {
- let typeinfo =
- context.write_any_type_info(TypeId::UNKNOWN as u32,
concrete_type_id)?;
- let serializer_fn = typeinfo.get_harness().get_write_data_fn();
- serializer_fn(value, context, has_generics)?;
+ let typeinfo = if write_type_info {
+ write_erased_any_type_info(context, concrete_type_id)?
} else {
- let serializer_fn = context
- .get_type_info(&concrete_type_id)?
- .get_harness()
- .get_write_data_fn();
- serializer_fn(value, context, has_generics)?;
- }
+ get_erased_any_type_info(context, &concrete_type_id)?
+ };
+ let serializer_fn = typeinfo.get_harness().get_write_data_fn();
+ serializer_fn(value, context, has_generics)?;
}
Ok(())
}
@@ -504,7 +550,6 @@ impl Serializer for Arc<dyn Any + Send + Sync> {
fn fory_is_shared_ref() -> bool {
true
}
-
fn fory_static_type_id() -> TypeId {
TypeId::UNKNOWN
}
@@ -519,7 +564,7 @@ impl Serializer for Arc<dyn Any + Send + Sync> {
Ok(())
}
- fn fory_read_data_send_sync(
+ fn fory_read_data_as_send_sync_any(
context: &mut ReadContext,
) -> Result<Box<dyn Any + Send + Sync>, Error>
where
@@ -574,15 +619,15 @@ pub fn read_arc_any(
Error::type_error("No type info found for read Arc<dyn Any
+ Send + Sync>")
})?
};
- // Check for generic container types which cannot be deserialized
polymorphically
- check_generic_container_type(&typeinfo)?;
+ check_erased_any_payload_type(&typeinfo)?;
let boxed = typeinfo
.get_harness()
- .read_polymorphic_data_send_sync(context, &typeinfo)?;
+ .read_polymorphic_data_as_send_sync_any(context, &typeinfo)?;
context.dec_depth();
Ok(Arc::<dyn Any + Send + Sync>::from(boxed))
}
RefFlag::RefValue => {
+ let ref_id = context.ref_reader.reserve_ref_id();
context.inc_depth()?;
let typeinfo = if read_type_info {
context.read_any_type_info()?
@@ -591,14 +636,13 @@ pub fn read_arc_any(
Error::type_error("No type info found for read Arc<dyn Any
+ Send + Sync>")
})?
};
- // Check for generic container types which cannot be deserialized
polymorphically
- check_generic_container_type(&typeinfo)?;
+ check_erased_any_payload_type(&typeinfo)?;
let boxed = typeinfo
.get_harness()
- .read_polymorphic_data_send_sync(context, &typeinfo)?;
+ .read_polymorphic_data_as_send_sync_any(context, &typeinfo)?;
context.dec_depth();
let arc: Arc<dyn Any + Send + Sync> = Arc::from(boxed);
- context.ref_reader.store_arc_ref(arc.clone());
+ context.ref_reader.store_arc_ref_at(ref_id, arc.clone());
Ok(arc)
}
}
diff --git a/rust/fory-core/src/serializer/arc.rs
b/rust/fory-core/src/serializer/arc.rs
index 5535dc706..1e69ae15c 100644
--- a/rust/fory-core/src/serializer/arc.rs
+++ b/rust/fory-core/src/serializer/arc.rs
@@ -28,15 +28,6 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static>
Serializer for Arc<T>
fn fory_is_shared_ref() -> bool {
true
}
-
- #[inline(always)]
- fn fory_is_threadsafe_type() -> bool
- where
- Self: Sized,
- {
- true
- }
-
fn fory_write(
&self,
context: &mut WriteContext,
@@ -129,7 +120,7 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static>
Serializer for Arc<T>
}
#[inline]
- fn fory_read_data_send_sync(
+ fn fory_read_data_as_send_sync_any(
context: &mut ReadContext,
) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
where
diff --git a/rust/fory-core/src/serializer/bool.rs
b/rust/fory-core/src/serializer/bool.rs
index 09307e168..dcc7bfbfd 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -35,6 +35,17 @@ impl Serializer for bool {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
Ok(context.reader.read_u8()? == 1)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/core.rs
b/rust/fory-core/src/serializer/core.rs
index 25a9f0c94..2f155792d 100644
--- a/rust/fory-core/src/serializer/core.rs
+++ b/rust/fory-core/src/serializer/core.rs
@@ -890,33 +890,20 @@ pub trait Serializer: 'static {
where
Self: Sized + ForyDefault;
- /// Whether this serialized value can be safely represented behind
- /// `Arc<dyn Any + Send + Sync>` after a dynamic read.
- ///
- /// The default is conservative. Implementations should return true only
- /// when the concrete value produced by this serializer is `Send + Sync`.
- #[inline(always)]
- fn fory_is_threadsafe_type() -> bool
- where
- Self: Sized,
- {
- false
- }
-
- /// Deserialize data for dynamic thread-safe carriers.
+ /// Deserialize data for dynamic send-sync carriers.
///
/// This method must construct the `Box<dyn Any + Send + Sync>` from the
/// concrete value before it is erased as `dyn Any`. It must never try to
/// upgrade a `Box<dyn Any>` returned by the ordinary dynamic read path.
#[inline(always)]
- fn fory_read_data_send_sync(
+ fn fory_read_data_as_send_sync_any(
context: &mut ReadContext,
) -> Result<Box<dyn Any + Send + Sync>, Error>
where
Self: Sized + ForyDefault,
{
let _ = context;
- Err(unsupported_threadsafe_type::<Self>())
+ Err(crate::error::unsupported_send_sync_type::<Self>())
}
/// Read and validate type metadata from the buffer.
@@ -1467,12 +1454,12 @@ pub trait StructSerializer: Serializer + 'static {
where
Self: Sized;
- /// Deserialize compatible data for dynamic thread-safe carriers.
+ /// Deserialize compatible data for dynamic send-sync carriers.
///
/// Implementations are generated only when the resulting struct/enum is
/// known to be `Send + Sync`.
#[inline(always)]
- fn fory_read_compatible_send_sync(
+ fn fory_read_compatible_as_send_sync_any(
context: &mut ReadContext,
type_info: Rc<TypeInfo>,
) -> Result<Box<dyn Any + Send + Sync>, Error>
@@ -1481,7 +1468,7 @@ pub trait StructSerializer: Serializer + 'static {
{
let _ = context;
let _ = type_info;
- Err(unsupported_threadsafe_type::<Self>())
+ Err(crate::error::unsupported_send_sync_type::<Self>())
}
}
@@ -1502,175 +1489,3 @@ pub fn write_data<T: Serializer>(this: &T, context:
&mut WriteContext) -> Result
pub fn read_data<T: Serializer + ForyDefault>(context: &mut ReadContext) ->
Result<T, Error> {
T::fory_read_data(context)
}
-
-#[inline(always)]
-pub fn box_send_sync<T>(value: T) -> Box<dyn Any + Send + Sync>
-where
- T: Any + Send + Sync,
-{
- Box::new(value)
-}
-
-#[inline(always)]
-pub(crate) fn is_known_threadsafe_static_type_id(type_id: TypeId) -> bool {
- matches!(
- type_id,
- TypeId::NONE
- | TypeId::BOOL
- | TypeId::INT8
- | TypeId::INT16
- | TypeId::INT32
- | TypeId::VARINT32
- | TypeId::INT64
- | TypeId::VARINT64
- | TypeId::TAGGED_INT64
- | TypeId::UINT8
- | TypeId::UINT16
- | TypeId::UINT32
- | TypeId::VAR_UINT32
- | TypeId::UINT64
- | TypeId::VAR_UINT64
- | TypeId::TAGGED_UINT64
- | TypeId::FLOAT16
- | TypeId::BFLOAT16
- | TypeId::FLOAT32
- | TypeId::FLOAT64
- | TypeId::STRING
- | TypeId::DURATION
- | TypeId::TIMESTAMP
- | TypeId::DATE
- | TypeId::DECIMAL
- | TypeId::BINARY
- | TypeId::BOOL_ARRAY
- | TypeId::INT8_ARRAY
- | TypeId::INT16_ARRAY
- | TypeId::INT32_ARRAY
- | TypeId::INT64_ARRAY
- | TypeId::UINT8_ARRAY
- | TypeId::UINT16_ARRAY
- | TypeId::UINT32_ARRAY
- | TypeId::UINT64_ARRAY
- | TypeId::FLOAT16_ARRAY
- | TypeId::BFLOAT16_ARRAY
- | TypeId::FLOAT32_ARRAY
- | TypeId::FLOAT64_ARRAY
- | TypeId::U128
- | TypeId::INT128
- | TypeId::USIZE
- | TypeId::ISIZE
- | TypeId::U128_ARRAY
- | TypeId::INT128_ARRAY
- | TypeId::USIZE_ARRAY
- | TypeId::ISIZE_ARRAY
- )
-}
-
-pub(crate) fn read_known_threadsafe_data<T>(
- context: &mut ReadContext,
-) -> Result<Box<dyn Any + Send + Sync>, Error>
-where
- T: Serializer + ForyDefault,
-{
- let boxed: Box<dyn Any> = Box::new(T::fory_read_data(context)?);
- box_known_threadsafe_data(T::fory_static_type_id(), boxed)
-}
-
-#[cold]
-#[inline(never)]
-fn unexpected_threadsafe_type_id(type_id: TypeId) -> Error {
- Error::type_error(format!(
- "deserialized value did not match thread-safe static type id {:?}",
- type_id
- ))
-}
-
-macro_rules! downcast_threadsafe_data {
- ($boxed:expr, $type_id:expr, $ty:ty) => {
- $boxed
- .downcast::<$ty>()
- .map(|value| value as Box<dyn Any + Send + Sync>)
- .map_err(|_| unexpected_threadsafe_type_id($type_id))
- };
-}
-
-fn box_known_threadsafe_data(
- type_id: TypeId,
- boxed: Box<dyn Any>,
-) -> Result<Box<dyn Any + Send + Sync>, Error> {
- match type_id {
- TypeId::NONE => downcast_threadsafe_data!(boxed, type_id, ()),
- TypeId::BOOL => downcast_threadsafe_data!(boxed, type_id, bool),
- TypeId::INT8 => downcast_threadsafe_data!(boxed, type_id, i8),
- TypeId::INT16 => downcast_threadsafe_data!(boxed, type_id, i16),
- TypeId::INT32 | TypeId::VARINT32 => downcast_threadsafe_data!(boxed,
type_id, i32),
- TypeId::INT64 | TypeId::VARINT64 | TypeId::TAGGED_INT64 => {
- downcast_threadsafe_data!(boxed, type_id, i64)
- }
- TypeId::UINT8 => downcast_threadsafe_data!(boxed, type_id, u8),
- TypeId::UINT16 => downcast_threadsafe_data!(boxed, type_id, u16),
- TypeId::UINT32 | TypeId::VAR_UINT32 =>
downcast_threadsafe_data!(boxed, type_id, u32),
- TypeId::UINT64 | TypeId::VAR_UINT64 | TypeId::TAGGED_UINT64 => {
- downcast_threadsafe_data!(boxed, type_id, u64)
- }
- TypeId::FLOAT16 => {
- downcast_threadsafe_data!(boxed, type_id,
crate::types::float16::float16)
- }
- TypeId::BFLOAT16 => {
- downcast_threadsafe_data!(boxed, type_id,
crate::types::bfloat16::bfloat16)
- }
- TypeId::FLOAT32 => downcast_threadsafe_data!(boxed, type_id, f32),
- TypeId::FLOAT64 => downcast_threadsafe_data!(boxed, type_id, f64),
- TypeId::STRING => downcast_threadsafe_data!(boxed, type_id, String),
- TypeId::DURATION => downcast_threadsafe_data!(boxed, type_id,
crate::types::Duration),
- TypeId::TIMESTAMP => downcast_threadsafe_data!(boxed, type_id,
crate::types::Timestamp),
- TypeId::DATE => downcast_threadsafe_data!(boxed, type_id,
crate::types::Date),
- TypeId::DECIMAL => downcast_threadsafe_data!(boxed, type_id,
crate::types::Decimal),
- TypeId::BINARY | TypeId::UINT8_ARRAY =>
downcast_threadsafe_data!(boxed, type_id, Vec<u8>),
- TypeId::BOOL_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<bool>),
- TypeId::INT8_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<i8>),
- TypeId::INT16_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<i16>),
- TypeId::INT32_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<i32>),
- TypeId::INT64_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<i64>),
- TypeId::UINT16_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<u16>),
- TypeId::UINT32_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<u32>),
- TypeId::UINT64_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<u64>),
- TypeId::FLOAT16_ARRAY => {
- downcast_threadsafe_data!(boxed, type_id,
Vec<crate::types::float16::float16>)
- }
- TypeId::BFLOAT16_ARRAY => {
- downcast_threadsafe_data!(boxed, type_id,
Vec<crate::types::bfloat16::bfloat16>)
- }
- TypeId::FLOAT32_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<f32>),
- TypeId::FLOAT64_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<f64>),
- TypeId::U128 => downcast_threadsafe_data!(boxed, type_id, u128),
- TypeId::INT128 => downcast_threadsafe_data!(boxed, type_id, i128),
- TypeId::USIZE => downcast_threadsafe_data!(boxed, type_id, usize),
- TypeId::ISIZE => downcast_threadsafe_data!(boxed, type_id, isize),
- TypeId::U128_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<u128>),
- TypeId::INT128_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<i128>),
- TypeId::USIZE_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<usize>),
- TypeId::ISIZE_ARRAY => downcast_threadsafe_data!(boxed, type_id,
Vec<isize>),
- _ => Err(unsupported_threadsafe_type_id(type_id)),
- }
-}
-
-#[cold]
-#[inline(never)]
-pub(crate) fn unsupported_threadsafe_type_id(type_id: TypeId) -> Error {
- Error::type_error(format!(
- "{:?} cannot be represented as Arc<dyn Any + Send + Sync>",
- type_id
- ))
-}
-
-#[cold]
-#[inline(never)]
-pub fn unsupported_threadsafe_type<T>() -> Error
-where
- T: ?Sized,
-{
- Error::type_error(format!(
- "{} cannot be represented as Arc<dyn Any + Send + Sync>",
- std::any::type_name::<T>()
- ))
-}
diff --git a/rust/fory-core/src/serializer/datetime.rs
b/rust/fory-core/src/serializer/datetime.rs
index dec6eaddb..3edb169b3 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -40,6 +40,17 @@ impl Serializer for Timestamp {
let nanos = context.reader.read_u32()?;
Timestamp::new(seconds, nanos)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -109,6 +120,17 @@ impl Serializer for Date {
};
Ok(Date::from_epoch_days(days))
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -168,6 +190,17 @@ impl Serializer for Duration {
let nanos = context.reader.read_i32()?;
Duration::new(seconds, nanos)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -228,6 +261,17 @@ mod chrono_support {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
Timestamp::fory_read_data(context)?.try_into()
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -282,6 +326,17 @@ mod chrono_support {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
Date::fory_read_data(context)?.try_into()
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -336,6 +391,17 @@ mod chrono_support {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
Duration::fory_read_data(context)?.try_into()
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/decimal.rs
b/rust/fory-core/src/serializer/decimal.rs
index 4775df108..0c1f88ece 100644
--- a/rust/fory-core/src/serializer/decimal.rs
+++ b/rust/fory-core/src/serializer/decimal.rs
@@ -39,6 +39,17 @@ impl Serializer for Decimal {
let unscaled = read_decimal_unscaled(&mut context.reader)?;
Ok(Self { unscaled, scale })
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
diff --git a/rust/fory-core/src/serializer/mod.rs
b/rust/fory-core/src/serializer/mod.rs
index 5de0bc095..5e18880c3 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -50,8 +50,5 @@ pub mod weak;
mod core;
mod decimal;
pub use any::{read_box_any, write_box_any};
-pub use core::{
- box_send_sync, read_data, unsupported_threadsafe_type, write_data,
ForyDefault, Serializer,
- StructSerializer,
-};
-pub(crate) use core::{is_known_threadsafe_static_type_id,
read_known_threadsafe_data};
+pub use core::{read_data, write_data, ForyDefault, Serializer,
StructSerializer};
+pub use util::send_sync::box_send_sync;
diff --git a/rust/fory-core/src/serializer/number.rs
b/rust/fory-core/src/serializer/number.rs
index d42d4b219..ebdb83a1f 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -40,6 +40,17 @@ macro_rules! impl_num_serializer {
fn fory_read_data(context: &mut ReadContext) -> Result<Self,
Error> {
$reader(&mut context.reader)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -114,6 +125,17 @@ impl Serializer for float16 {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
Reader::read_f16(&mut context.reader)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
std::mem::size_of::<float16>()
@@ -162,6 +184,17 @@ impl Serializer for bfloat16 {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
Reader::read_bf16(&mut context.reader)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
std::mem::size_of::<bfloat16>()
diff --git a/rust/fory-core/src/serializer/string.rs
b/rust/fory-core/src/serializer/string.rs
index abbb82d93..79771fd0c 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -66,6 +66,17 @@ impl Serializer for String {
}?;
Ok(s)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/tuple.rs
b/rust/fory-core/src/serializer/tuple.rs
index a0789a906..bbda7383b 100644
--- a/rust/fory-core/src/serializer/tuple.rs
+++ b/rust/fory-core/src/serializer/tuple.rs
@@ -38,6 +38,17 @@ impl Serializer for () {
// Unit type has no data to read
Ok(())
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/unknown_case.rs
b/rust/fory-core/src/serializer/unknown_case.rs
index 7423f384e..15a658a82 100644
--- a/rust/fory-core/src/serializer/unknown_case.rs
+++ b/rust/fory-core/src/serializer/unknown_case.rs
@@ -18,7 +18,7 @@
use crate::context::{ReadContext, WriteContext};
use crate::error::Error;
use crate::resolver::{RefFlag, RefMode};
-use crate::serializer::any::check_generic_container_type;
+use crate::serializer::any::check_erased_any_payload_type;
use crate::serializer::{ForyDefault, Serializer};
use crate::type_id::{self, TypeId};
use crate::types::UnknownCase;
@@ -154,10 +154,10 @@ pub fn read_payload(context: &mut ReadContext, case_id:
u32) -> Result<UnknownCa
// boundary by itself, and real nested payload serializers perform
their
// own depth checks.
let type_info = context.read_any_type_info()?;
- check_generic_container_type(&type_info)?;
+ check_erased_any_payload_type(&type_info)?;
let boxed = type_info
.get_harness()
- .read_polymorphic_data_send_sync(context, &type_info)?;
+ .read_polymorphic_data_as_send_sync_any(context, &type_info)?;
let value: Arc<dyn std::any::Any + Send + Sync> = Arc::from(boxed);
if let Some(ref_id) = ref_id {
context.ref_reader.store_arc_ref_at(ref_id, value.clone());
@@ -207,12 +207,7 @@ impl Serializer for UnknownCase {
fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
read_payload(context, 0)
}
-
- fn fory_is_threadsafe_type() -> bool {
- true
- }
-
- fn fory_read_data_send_sync(
+ fn fory_read_data_as_send_sync_any(
context: &mut ReadContext,
) -> Result<Box<dyn Any + Send + Sync>, Error> {
Ok(crate::serializer::box_send_sync(read_payload(context, 0)?))
diff --git a/rust/fory-core/src/serializer/unsigned_number.rs
b/rust/fory-core/src/serializer/unsigned_number.rs
index e5f49d008..6c679df7f 100644
--- a/rust/fory-core/src/serializer/unsigned_number.rs
+++ b/rust/fory-core/src/serializer/unsigned_number.rs
@@ -38,6 +38,17 @@ macro_rules! impl_xlang_unsigned_num_serializer {
fn fory_read_data(context: &mut ReadContext) -> Result<Self,
Error> {
$reader(&mut context.reader)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
@@ -104,6 +115,17 @@ macro_rules! impl_rust_unsigned_num_serializer {
fn fory_read_data(context: &mut ReadContext) -> Result<Self,
Error> {
$reader(&mut context.reader)
}
+ #[inline]
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn std::any::Any + Send + Sync>, Error>
+ where
+ Self: Sized + ForyDefault,
+ {
+ Ok(crate::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
#[inline(always)]
fn fory_reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/util.rs
b/rust/fory-core/src/serializer/util.rs
index d339cc972..559713261 100644
--- a/rust/fory-core/src/serializer/util.rs
+++ b/rust/fory-core/src/serializer/util.rs
@@ -95,3 +95,15 @@ pub fn write_dyn_data_generic<T: Serializer>(
.get_write_data_fn();
serializer_fn(any_value, context, has_generics)
}
+
+pub(crate) mod send_sync {
+ use std::any::Any;
+
+ #[inline(always)]
+ pub fn box_send_sync<T>(value: T) -> Box<dyn Any + Send + Sync>
+ where
+ T: Any + Send + Sync,
+ {
+ Box::new(value)
+ }
+}
diff --git a/rust/fory-core/src/types/unknown_case.rs
b/rust/fory-core/src/types/unknown_case.rs
index 0496ef4f4..cc9a8c5e3 100644
--- a/rust/fory-core/src/types/unknown_case.rs
+++ b/rust/fory-core/src/types/unknown_case.rs
@@ -27,7 +27,7 @@ pub struct UnknownCase {
type_id: u32,
// Keep resolver TypeInfo/Rc out of the carrier. Generated unions can
outlive or move
// independently from the resolver context, so the carrier stores only
stable metadata
- // plus a dynamic payload whose thread-safety is guaranteed by the trait
object.
+ // plus a dynamic payload whose `Send + Sync` guarantee is carried by the
trait object.
value: Arc<dyn Any + Send + Sync>,
}
diff --git a/rust/fory-derive/src/lib.rs b/rust/fory-derive/src/lib.rs
index 932c9c15a..b337e5b3a 100644
--- a/rust/fory-derive/src/lib.rs
+++ b/rust/fory-derive/src/lib.rs
@@ -143,6 +143,11 @@
//! **Custom Types:**
//! - Any type that implements `Serializer` (for `Fory`) or `Row` (for
`ForyRow`)
//!
+//! Derived structs, enums, and unions can be used behind
+//! `Arc<dyn Any + Send + Sync>` when the concrete type satisfies `Send +
Sync`.
+//! Known non-`Send + Sync` field types such as `Rc<T>` and `RefCell<T>` are
not
+//! eligible for that carrier.
+//!
//! ## Usage with Fory
//!
//! After deriving the macros, you can use the types with the Fory
serialization
@@ -340,6 +345,8 @@ fn parse_fory_attrs(attrs: &[Attribute]) ->
syn::Result<ForyAttrs> {
Some(_) => evolving_flag,
None => Some(value),
};
+ } else {
+ return Err(meta.error("unsupported type-level fory
attribute"));
}
Ok(())
})?;
diff --git a/rust/fory-derive/src/object/field_codec.rs
b/rust/fory-derive/src/object/field_codec.rs
index 8543e044d..f310f4600 100644
--- a/rust/fory-derive/src/object/field_codec.rs
+++ b/rust/fory-derive/src/object/field_codec.rs
@@ -766,7 +766,7 @@ pub(crate) fn codec_type_for(
);
}
return Ok(quote! {
- compile_error!("Arc<dyn Any> is not a shared thread-safe carrier;
use Arc<dyn Any + Send + Sync>")
+ compile_error!("Arc<dyn Any> is not a shared Send + Sync carrier;
use Arc<dyn Any + Send + Sync>")
});
}
diff --git a/rust/fory-derive/src/object/serializer.rs
b/rust/fory-derive/src/object/serializer.rs
index 5721dd9dc..9dc9dcd8c 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -52,8 +52,8 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs:
ForyAttrs) -> TokenStrea
} else {
quote! {}
};
- let threadsafe_tokens = generate_threadsafe_tokens(ast);
- let serializer_threadsafe_ts = threadsafe_tokens.serializer.clone();
+ let send_sync_tokens = generate_send_sync_tokens(ast);
+ let serializer_send_sync_ts = send_sync_tokens.serializer.clone();
// StructSerializer
let (
@@ -62,7 +62,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs:
ForyAttrs) -> TokenStrea
fields_info_ts,
variants_fields_info_ts,
read_compatible_ts,
- read_compatible_send_sync_ts,
+ read_compatible_as_send_sync_any_ts,
enum_variant_meta_types,
) = match &ast.data {
syn::Data::Struct(s) => {
@@ -79,7 +79,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs:
ForyAttrs) -> TokenStrea
misc::gen_field_fields_info(&source_fields),
quote! { ::std::result::Result::Ok(::std::vec::Vec::new()) },
// No variants for structs
read::gen_read_compatible(&source_fields),
- threadsafe_tokens.struct_read_compatible.clone(),
+ send_sync_tokens.struct_read_compatible.clone(),
vec![], // No variant meta types for structs
)
}
@@ -185,7 +185,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs:
ForyAttrs) -> TokenStrea
#read_compatible_ts
}
- #read_compatible_send_sync_ts
+ #read_compatible_as_send_sync_any_ts
}
impl #impl_generics ::fory_core::Serializer for #name #ty_generics
#where_clause {
@@ -250,7 +250,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs:
ForyAttrs) -> TokenStrea
#read_data_ts
}
- #serializer_threadsafe_ts
+ #serializer_send_sync_ts
#[inline(always)]
fn fory_read_type_info(context: &mut ::fory_core::ReadContext) ->
::std::result::Result<(), ::fory_core::error::Error> {
@@ -263,14 +263,14 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs:
ForyAttrs) -> TokenStrea
code
}
-struct ThreadsafeTokens {
+struct SendSyncTokens {
serializer: proc_macro2::TokenStream,
struct_read_compatible: proc_macro2::TokenStream,
}
-fn generate_threadsafe_tokens(ast: &syn::DeriveInput) -> ThreadsafeTokens {
- if !derive_type_is_threadsafe(ast) {
- return ThreadsafeTokens {
+fn generate_send_sync_tokens(ast: &syn::DeriveInput) -> SendSyncTokens {
+ if !derive_type_is_send_sync(ast) {
+ return SendSyncTokens {
serializer: quote! {},
struct_read_compatible: quote! {},
};
@@ -278,7 +278,7 @@ fn generate_threadsafe_tokens(ast: &syn::DeriveInput) ->
ThreadsafeTokens {
let struct_read_compatible = if matches!(ast.data, syn::Data::Struct(_)) {
quote! {
#[inline]
- fn fory_read_compatible_send_sync(
+ fn fory_read_compatible_as_send_sync_any(
context: &mut ::fory_core::ReadContext,
type_info: ::std::rc::Rc<::fory_core::TypeInfo>,
) -> ::std::result::Result<::std::boxed::Box<dyn ::std::any::Any +
Send + Sync>, ::fory_core::error::Error> {
@@ -289,18 +289,10 @@ fn generate_threadsafe_tokens(ast: &syn::DeriveInput) ->
ThreadsafeTokens {
} else {
quote! {}
};
- ThreadsafeTokens {
+ SendSyncTokens {
serializer: quote! {
- #[inline(always)]
- fn fory_is_threadsafe_type() -> bool
- where
- Self: Sized,
- {
- true
- }
-
#[inline]
- fn fory_read_data_send_sync(
+ fn fory_read_data_as_send_sync_any(
context: &mut ::fory_core::ReadContext,
) -> ::std::result::Result<::std::boxed::Box<dyn ::std::any::Any +
Send + Sync>, ::fory_core::error::Error>
where
@@ -314,14 +306,14 @@ fn generate_threadsafe_tokens(ast: &syn::DeriveInput) ->
ThreadsafeTokens {
}
}
-fn derive_type_is_threadsafe(ast: &syn::DeriveInput) -> bool {
+fn derive_type_is_send_sync(ast: &syn::DeriveInput) -> bool {
use crate::object::util::{
- all_type_params_send_sync, type_is_threadsafe,
type_param_send_sync_bounds,
+ all_type_params_send_sync, type_is_send_sync,
type_param_send_sync_bounds,
};
- // This is a syntactic filter for generating the send-sync reader. The
- // generated reader still boxes `Self`, so Rust enforces the final
- // `Send + Sync` invariant for nested user-defined field types.
+ // This syntactic filter rejects field types that are known not to satisfy
+ // `Send + Sync`. Opaque custom field types are allowed through, and Rust
+ // validates the final `Self: Send + Sync` bound when compiling the reader.
if !all_type_params_send_sync(&ast.generics) {
return false;
}
@@ -330,12 +322,12 @@ fn derive_type_is_threadsafe(ast: &syn::DeriveInput) ->
bool {
syn::Data::Struct(data) => data
.fields
.iter()
- .all(|field| type_is_threadsafe(&field.ty, &send_sync_params)),
+ .all(|field| type_is_send_sync(&field.ty, &send_sync_params)),
syn::Data::Enum(data) => data.variants.iter().all(|variant| {
variant
.fields
.iter()
- .all(|field| type_is_threadsafe(&field.ty, &send_sync_params))
+ .all(|field| type_is_send_sync(&field.ty, &send_sync_params))
}),
syn::Data::Union(_) => false,
}
diff --git a/rust/fory-derive/src/object/util.rs
b/rust/fory-derive/src/object/util.rs
index 4fc38b893..260159b82 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -963,13 +963,13 @@ pub(crate) fn all_type_params_send_sync(generics:
&syn::Generics) -> bool {
.all(|param| bounded.contains(¶m.ident.to_string()))
}
-pub(crate) fn type_is_threadsafe(ty: &Type, send_sync_params:
&HashSet<String>) -> bool {
+pub(crate) fn type_is_send_sync(ty: &Type, send_sync_params: &HashSet<String>)
-> bool {
match ty {
- Type::Array(array) => type_is_threadsafe(array.elem.as_ref(),
send_sync_params),
+ Type::Array(array) => type_is_send_sync(array.elem.as_ref(),
send_sync_params),
Type::Tuple(tuple) => tuple
.elems
.iter()
- .all(|elem| type_is_threadsafe(elem, send_sync_params)),
+ .all(|elem| type_is_send_sync(elem, send_sync_params)),
Type::Path(type_path) => {
let Some(segment) = type_path.path.segments.last() else {
return false;
@@ -996,15 +996,15 @@ pub(crate) fn type_is_threadsafe(ty: &Type,
send_sync_params: &HashSet<String>)
trait_object_is_any_send_sync(trait_obj)
}
(_, Type::TraitObject(_)) => false,
- _ => type_is_threadsafe(inner, send_sync_params),
+ _ => type_is_send_sync(inner, send_sync_params),
}
}
"HashMap" | "BTreeMap" => {
let Some((key, value)) =
two_path_type_args(&segment.arguments) else {
return false;
};
- type_is_threadsafe(key, send_sync_params)
- && type_is_threadsafe(value, send_sync_params)
+ type_is_send_sync(key, send_sync_params)
+ && type_is_send_sync(value, send_sync_params)
}
_ => true,
}
diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs
index e190d0c8d..70327c2a9 100644
--- a/rust/fory/src/lib.rs
+++ b/rust/fory/src/lib.rs
@@ -340,7 +340,8 @@
//! - `Box<dyn Trait>` - Owned trait objects
//! - `Rc<dyn Trait>` - Reference-counted trait objects
//! - `Arc<dyn Trait>` - Thread-safe reference-counted trait objects
-//! - `Rc<dyn Any>` / `Arc<dyn Any + Send + Sync>` - Runtime type dispatch
without custom traits
+//! - `Box<dyn Any>` / `Rc<dyn Any>` / `Arc<dyn Any + Send + Sync>` - Runtime
type
+//! dispatch without custom traits for registered non-container payloads
//! - Collections: `Vec<Box<dyn Trait>>`, `HashMap<K, Box<dyn Trait>>`
//!
//! #### Basic Trait Object Serialization
@@ -401,7 +402,7 @@
//!
//! #### Serializing `dyn Any` Trait Objects
//!
-//! **What it does:** Supports serializing `Rc<dyn Any>` and
+//! **What it does:** Supports serializing `Box<dyn Any>`, `Rc<dyn Any>`, and
//! `Arc<dyn Any + Send + Sync>` for maximum runtime type flexibility without
//! defining custom traits.
//!
@@ -410,7 +411,7 @@
//!
//! **Key points:**
//!
-//! - Works with any type that implements `Serializer`
+//! - Works with registered concrete non-container types that implement
`Serializer`
//! - Requires downcasting after deserialization to access the concrete type
//! - Type information is preserved during serialization
//!
@@ -470,6 +471,18 @@
//! # }
//! ```
//!
+//! `Box<dyn Any>`, `Rc<dyn Any>`, and `Arc<dyn Any + Send + Sync>` are
supported
+//! erased `Any` carriers for registered concrete non-container payloads.
+//! Use `Arc<dyn Any + Send + Sync>` when the erased payload must be shareable
+//! across threads; the concrete payload type must also satisfy `Send + Sync`.
+//! Registered structs, enums, and unions that satisfy those bounds can be used
+//! as the erased payload.
+//! Generic containers such as `Vec<T>`, `HashMap<K, V>`, `HashSet<T>`, and
+//! `LinkedList<T>` are not supported directly as top-level erased `Any`
+//! payloads behind any of those carriers. This also includes primitive vector
+//! encodings such as `Vec<u8>`. Wrap the container in a registered derived
type
+//! when it needs to travel behind an erased `Any` carrier.
+//!
//! #### Rc/Arc-Based Trait Objects in Structs
//!
//! For struct fields containing `Rc<dyn Trait>` or `Arc<dyn Trait>`, Apache
Fory™
@@ -536,8 +549,9 @@
//! `Serializer` directly. For standalone serialization (not inside struct
fields),
//! the `register_trait_type!` macro generates wrapper types.
//!
-//! **Note:** If you don't want to use wrapper types, you can serialize as
`Rc<dyn Any>`
-//! or `Arc<dyn Any + Send + Sync>` instead (see the `dyn Any` section above).
+//! **Note:** If you don't want to use wrapper types for concrete
non-container payloads,
+//! you can serialize as `Box<dyn Any>`, `Rc<dyn Any>`, or
+//! `Arc<dyn Any + Send + Sync>` instead (see the `dyn Any` section above).
//!
//! The `register_trait_type!` macro generates `AnimalRc` and `AnimalArc`
wrapper types:
//!
@@ -1005,8 +1019,11 @@
//! - `Box<dyn Trait>` - Owned trait objects
//! - `Rc<dyn Trait>` - Reference-counted trait objects
//! - `Arc<dyn Trait>` - Thread-safe reference-counted trait objects
-//! - `Rc<dyn Any>` - Runtime type dispatch without custom traits
-//! - `Arc<dyn Any + Send + Sync>` - Thread-safe runtime type dispatch
+//! - `Box<dyn Any>` - Runtime type dispatch for registered non-container
payloads
+//! - `Rc<dyn Any>` - Reference-counted runtime type dispatch for registered
+//! non-container payloads
+//! - `Arc<dyn Any + Send + Sync>` - Thread-safe runtime type dispatch for
+//! registered non-container payloads
//!
//! ## Wire Modes And Schema Evolution
//!
diff --git a/rust/tests/tests/test_any.rs b/rust/tests/tests/test_any.rs
index d7c4a7b57..8bc978c52 100644
--- a/rust/tests/tests/test_any.rs
+++ b/rust/tests/tests/test_any.rs
@@ -16,13 +16,113 @@
// under the License.
use fory_core::fory::Fory;
-use fory_derive::ForyStruct;
+use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
use std::any::Any;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet, LinkedList};
use std::rc::Rc;
use std::sync::Arc;
use std::vec;
+fn assert_erased_container_error(message: String) {
+ assert!(
+ message.contains("top-level erased Any")
+ || message.contains("Erased Any payloads require")
+ || message.contains("not found in type_info registry"),
+ "unexpected error: {message}"
+ );
+}
+
+fn assert_box_any_unsupported<T: 'static>(fory: &Fory, value: T) {
+ let wrapped: Box<dyn Any> = Box::new(value);
+ match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Box<dyn Any>, _> = fory.deserialize(&bytes);
+ let err = result.expect_err("expected Box<dyn Any> container read
to fail");
+ assert_erased_container_error(err.to_string());
+ }
+ Err(err) => assert_erased_container_error(err.to_string()),
+ }
+}
+
+fn assert_rc_any_unsupported<T: 'static>(fory: &Fory, value: T) {
+ let wrapped: Rc<dyn Any> = Rc::new(value);
+ match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Rc<dyn Any>, _> = fory.deserialize(&bytes);
+ let err = result.expect_err("expected Rc<dyn Any> container read
to fail");
+ assert_erased_container_error(err.to_string());
+ }
+ Err(err) => assert_erased_container_error(err.to_string()),
+ }
+}
+
+fn assert_arc_any_unsupported<T: 'static + Send + Sync>(fory: &Fory, value: T)
{
+ let wrapped: Arc<dyn Any + Send + Sync> = Arc::new(value);
+ match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Arc<dyn Any + Send + Sync>, _> =
fory.deserialize(&bytes);
+ let err = result.expect_err("expected Arc<dyn Any> container read
to fail");
+ assert_erased_container_error(err.to_string());
+ }
+ Err(err) => assert_erased_container_error(err.to_string()),
+ }
+}
+
+fn assert_box_any_values_unsupported(fory: &Fory, values: Vec<Box<dyn Any>>) {
+ let result = fory
+ .serialize(&values)
+ .and_then(|bytes| fory.deserialize::<Vec<Box<dyn
Any>>>(&bytes).map(|_| ()));
+ let err = result.expect_err("expected Box<dyn Any> container values to
fail");
+ assert_erased_container_error(err.to_string());
+}
+
+fn assert_rc_any_values_unsupported(fory: &Fory, values: Vec<Rc<dyn Any>>) {
+ let result = fory
+ .serialize(&values)
+ .and_then(|bytes| fory.deserialize::<Vec<Rc<dyn Any>>>(&bytes).map(|_|
()));
+ let err = result.expect_err("expected Rc<dyn Any> container values to
fail");
+ assert_erased_container_error(err.to_string());
+}
+
+fn assert_arc_any_values_unsupported(fory: &Fory, values: Vec<Arc<dyn Any +
Send + Sync>>) {
+ let result = fory.serialize(&values).and_then(|bytes| {
+ fory.deserialize::<Vec<Arc<dyn Any + Send + Sync>>>(&bytes)
+ .map(|_| ())
+ });
+ let err = result.expect_err("expected Arc<dyn Any> container values to
fail");
+ assert_erased_container_error(err.to_string());
+}
+
+fn assert_box_any_map_unsupported(fory: &Fory, values: HashMap<String, Box<dyn
Any>>) {
+ let result = fory.serialize(&values).and_then(|bytes| {
+ fory.deserialize::<HashMap<String, Box<dyn Any>>>(&bytes)
+ .map(|_| ())
+ });
+ let err = result.expect_err("expected Box<dyn Any> map values to fail");
+ assert_erased_container_error(err.to_string());
+}
+
+fn assert_rc_any_map_unsupported(fory: &Fory, values: HashMap<String, Rc<dyn
Any>>) {
+ let result = fory.serialize(&values).and_then(|bytes| {
+ fory.deserialize::<HashMap<String, Rc<dyn Any>>>(&bytes)
+ .map(|_| ())
+ });
+ let err = result.expect_err("expected Rc<dyn Any> map values to fail");
+ assert_erased_container_error(err.to_string());
+}
+
+fn assert_arc_any_map_unsupported(
+ fory: &Fory,
+ values: HashMap<String, Arc<dyn Any + Send + Sync>>,
+) {
+ let result = fory.serialize(&values).and_then(|bytes| {
+ fory.deserialize::<HashMap<String, Arc<dyn Any + Send + Sync>>>(&bytes)
+ .map(|_| ())
+ });
+ let err = result.expect_err("expected Arc<dyn Any> map values to fail");
+ assert_erased_container_error(err.to_string());
+}
+
#[test]
fn test_box_dyn_any() {
let fory = Fory::builder().xlang(false).build();
@@ -90,13 +190,10 @@ fn test_arc_dyn_any() {
let deserialized2: Arc<dyn Any + Send + Sync> =
fory.deserialize(&bytes2).unwrap();
assert_eq!(deserialized2.downcast_ref::<i32>().unwrap(), &123i32);
- let value3: Arc<dyn Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
+ let value3: Arc<dyn Any + Send + Sync> = Arc::new(true);
let bytes3 = fory.serialize(&value3).unwrap();
let deserialized3: Arc<dyn Any + Send + Sync> =
fory.deserialize(&bytes3).unwrap();
- assert_eq!(
- deserialized3.downcast_ref::<Vec<i32>>().unwrap(),
- &vec![1, 2, 3]
- );
+ assert_eq!(deserialized3.downcast_ref::<bool>().unwrap(), &true);
}
#[test]
@@ -122,17 +219,17 @@ fn test_rc_dyn_any_shared_reference() {
fn test_arc_dyn_any_shared_reference() {
let fory = Fory::builder().xlang(false).build();
- let shared_vec: Arc<dyn Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
+ let shared_vec: Arc<dyn Any + Send + Sync> =
Arc::new("shared".to_string());
let data = vec![shared_vec.clone(), shared_vec.clone()];
let bytes = fory.serialize(&data).unwrap();
let deserialized: Vec<Arc<dyn Any + Send + Sync>> =
fory.deserialize(&bytes).unwrap();
- let first_vec = deserialized[0].downcast_ref::<Vec<i32>>().unwrap();
- let second_vec = deserialized[1].downcast_ref::<Vec<i32>>().unwrap();
- assert_eq!(first_vec, &vec![1, 2, 3]);
- assert_eq!(second_vec, &vec![1, 2, 3]);
+ let first_vec = deserialized[0].downcast_ref::<String>().unwrap();
+ let second_vec = deserialized[1].downcast_ref::<String>().unwrap();
+ assert_eq!(first_vec, "shared");
+ assert_eq!(second_vec, "shared");
assert_eq!(Arc::strong_count(&shared_vec), 3);
}
@@ -207,6 +304,197 @@ struct Container {
items: Vec<String>,
}
+#[derive(ForyStruct, PartialEq, Debug)]
+struct RcRefPayload {
+ name: String,
+ shared: Rc<String>,
+}
+
+#[derive(ForyStruct, PartialEq, Debug)]
+struct ArcRefPayload {
+ name: String,
+ shared: Arc<String>,
+}
+
+#[test]
+fn wrapped_container_box_any() {
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register_by_name::<Container>("", "Container").unwrap();
+
+ let value: Box<dyn Any> = Box::new(Container {
+ id: 321,
+ items: vec!["wrapped".to_string(), "values".to_string()],
+ });
+ let bytes = fory.serialize(&value).unwrap();
+ let decoded: Box<dyn Any> = fory.deserialize(&bytes).unwrap();
+ let result = decoded.downcast_ref::<Container>().unwrap();
+
+ assert_eq!(result.id, 321);
+ assert_eq!(result.items, vec!["wrapped", "values"]);
+}
+
+#[test]
+fn rc_any_refvalue_keeps_outer_ref() {
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register_by_name::<RcRefPayload>("", "RcRefPayload")
+ .unwrap();
+
+ let payload: Rc<dyn Any> = Rc::new(RcRefPayload {
+ name: "outer".to_string(),
+ shared: Rc::new("nested".to_string()),
+ });
+ let values = vec![payload.clone(), payload.clone()];
+
+ let bytes = fory.serialize(&values).unwrap();
+ let decoded: Vec<Rc<dyn Any>> = fory.deserialize(&bytes).unwrap();
+
+ assert!(Rc::ptr_eq(&decoded[0], &decoded[1]));
+ let payload = decoded[0].downcast_ref::<RcRefPayload>().unwrap();
+ assert_eq!(payload.name, "outer");
+ assert_eq!(payload.shared.as_str(), "nested");
+}
+
+#[test]
+fn arc_any_refvalue_keeps_outer_ref() {
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register_by_name::<ArcRefPayload>("", "ArcRefPayload")
+ .unwrap();
+
+ let payload: Arc<dyn Any + Send + Sync> = Arc::new(ArcRefPayload {
+ name: "outer".to_string(),
+ shared: Arc::new("nested".to_string()),
+ });
+ let values = vec![payload.clone(), payload.clone()];
+
+ let bytes = fory.serialize(&values).unwrap();
+ let decoded: Vec<Arc<dyn Any + Send + Sync>> =
fory.deserialize(&bytes).unwrap();
+
+ assert!(Arc::ptr_eq(&decoded[0], &decoded[1]));
+ let payload = decoded[0].downcast_ref::<ArcRefPayload>().unwrap();
+ assert_eq!(payload.name, "outer");
+ assert_eq!(payload.shared.as_str(), "nested");
+}
+
+#[test]
+fn generic_containers_rejected_in_any() {
+ let fory = Fory::builder().xlang(false).build();
+
+ assert_box_any_unsupported(&fory, vec![1_i32, 2, 3]);
+ assert_rc_any_unsupported(&fory, vec![1_i32, 2, 3]);
+ assert_arc_any_unsupported(&fory, vec![1_i32, 2, 3]);
+
+ assert_box_any_unsupported(&fory, LinkedList::from([1_i32, 2, 3]));
+ assert_rc_any_unsupported(&fory, HashSet::from([1_i32, 2, 3]));
+ assert_arc_any_unsupported(
+ &fory,
+ HashMap::from([("one".to_string(), 1_i32), ("two".to_string(), 2)]),
+ );
+}
+
+#[test]
+fn any_collection_rejects_containers() {
+ let fory = Fory::builder().xlang(false).build();
+
+ assert_box_any_values_unsupported(&fory, vec![Box::new(vec![1_i32, 2, 3])
as Box<dyn Any>]);
+ assert_box_any_values_unsupported(
+ &fory,
+ vec![
+ Box::new(vec![1_i32, 2, 3]) as Box<dyn Any>,
+ Box::new(vec![4_i32, 5, 6]) as Box<dyn Any>,
+ ],
+ );
+
+ assert_rc_any_values_unsupported(&fory, vec![Rc::new(vec![1_i32, 2, 3]) as
Rc<dyn Any>]);
+ assert_rc_any_values_unsupported(
+ &fory,
+ vec![
+ Rc::new(vec![1_i32, 2, 3]) as Rc<dyn Any>,
+ Rc::new(vec![4_i32, 5, 6]) as Rc<dyn Any>,
+ ],
+ );
+
+ assert_arc_any_values_unsupported(
+ &fory,
+ vec![Arc::new(vec![1_i32, 2, 3]) as Arc<dyn Any + Send + Sync>],
+ );
+ assert_arc_any_values_unsupported(
+ &fory,
+ vec![
+ Arc::new(vec![1_i32, 2, 3]) as Arc<dyn Any + Send + Sync>,
+ Arc::new(vec![4_i32, 5, 6]) as Arc<dyn Any + Send + Sync>,
+ ],
+ );
+}
+
+#[test]
+fn any_map_values_reject_containers() {
+ let fory = Fory::builder().xlang(false).build();
+
+ assert_box_any_map_unsupported(
+ &fory,
+ HashMap::from([(
+ "list".to_string(),
+ Box::new(vec![1_i32, 2, 3]) as Box<dyn Any>,
+ )]),
+ );
+ assert_rc_any_map_unsupported(
+ &fory,
+ HashMap::from([(
+ "map".to_string(),
+ Rc::new(HashMap::from([("one".to_string(), 1_i32)])) as Rc<dyn
Any>,
+ )]),
+ );
+ assert_arc_any_map_unsupported(
+ &fory,
+ HashMap::from([(
+ "list".to_string(),
+ Arc::new(vec![1_i32, 2, 3]) as Arc<dyn Any + Send + Sync>,
+ )]),
+ );
+}
+
+#[test]
+fn compatible_enum_box_any_read() {
+ #[derive(ForyEnum, Debug, Default, PartialEq)]
+ enum Status {
+ #[default]
+ Active,
+ Inactive,
+ }
+
+ let mut fory = Fory::builder().xlang(false).compatible(true).build();
+ fory.register::<Status>(710).unwrap();
+
+ let value: Box<dyn Any> = Box::new(Status::Inactive);
+ let bytes = fory.serialize(&value).unwrap();
+ let decoded: Box<dyn Any> = fory.deserialize(&bytes).unwrap();
+
+ assert_eq!(decoded.downcast_ref::<Status>().unwrap(), &Status::Inactive);
+}
+
+#[test]
+fn compatible_union_rc_any_read() {
+ #[derive(ForyUnion, Debug, PartialEq)]
+ enum Event {
+ #[fory(unknown)]
+ Unknown(fory_core::UnknownCase),
+ #[fory(id = 0, default)]
+ Value(String),
+ }
+
+ let mut fory = Fory::builder().xlang(false).compatible(true).build();
+ fory.register_union::<Event>(711).unwrap();
+
+ let value: Rc<dyn Any> = Rc::new(Event::Value("compatible".to_string()));
+ let bytes = fory.serialize(&value).unwrap();
+ let decoded: Rc<dyn Any> = fory.deserialize(&bytes).unwrap();
+
+ assert_eq!(
+ decoded.downcast_ref::<Event>().unwrap(),
+ &Event::Value("compatible".to_string())
+ );
+}
+
#[derive(ForyStruct)]
struct ArcAnyHolder {
value: Arc<dyn Any + Send + Sync>,
@@ -348,36 +636,18 @@ struct StructB {
i: i32,
}
-/// Test that serializing different Vec<T> types in Box<dyn Any> returns a
clear error.
+/// Test that different Vec<T> types in Box<dyn Any> return a clear error.
///
-/// This tests for a known limitation of the xlang serialization protocol:
-/// different generic container types (Vec<StructA>, Vec<StructB>) share the
same
-/// type ID (LIST=21), so they cannot be distinguished during polymorphic
deserialization.
+/// Different generic container instantiations share broad container metadata,
so
+/// they cannot be used as unambiguous top-level erased Any payloads.
///
/// Previously this caused non-deterministic failures. Now it returns a clear
error message.
#[test]
-fn test_vec_of_different_struct_types_in_box_any_returns_error() {
+fn generic_vecs_rejected_in_box_any() {
let mut fory = Fory::builder().xlang(false).build();
- // Register both struct types
fory.register_by_name::<StructA>("", "StructA").unwrap();
- fory.register_generic_trait::<Vec<StructA>>().unwrap();
fory.register_by_name::<StructB>("", "StructB").unwrap();
- fory.register_generic_trait::<Vec<StructB>>().unwrap();
-
- // Create Box<dyn Any> wrappers for Vec<StructA> and Vec<StructB>
- let a_vec: Box<dyn Any> = Box::new(vec![StructA { a: 11 }; 5]);
- let b_vec: Box<dyn Any> = Box::new(vec![StructB { i: 1 }; 5]);
- let any_vec: Vec<Box<dyn Any>> = vec![a_vec, b_vec];
- let bytes = fory.serialize(&any_vec).unwrap();
-
- // Deserialization should fail with a clear error about generic containers
- let result: Result<Vec<Box<dyn Any>>, _> = fory.deserialize(&bytes);
- assert!(result.is_err());
- let err_msg = result.unwrap_err().to_string();
- assert!(
- err_msg.contains("generic container types"),
- "Expected error about generic containers, got: {}",
- err_msg
- );
+ assert_box_any_unsupported(&fory, vec![StructA { a: 11 }; 5]);
+ assert_box_any_unsupported(&fory, vec![StructB { i: 1 }; 5]);
}
diff --git a/rust/tests/tests/test_collection.rs
b/rust/tests/tests/test_collection.rs
index 9271e39ae..c71e4af20 100644
--- a/rust/tests/tests/test_collection.rs
+++ b/rust/tests/tests/test_collection.rs
@@ -15,23 +15,20 @@
// specific language governing permissions and limitations
// under the License.
-use fory_core::{Fory, Serializer};
+use fory_core::Fory;
use fory_derive::ForyStruct;
use std::collections::{BTreeSet, BinaryHeap, HashSet};
#[test]
fn test_btreeset_roundtrip() {
- let mut fory: Fory = Fory::builder().xlang(false).build();
- fory.register_generic_trait::<BTreeSet<i32>>().unwrap();
+ let fory: Fory = Fory::builder().xlang(false).build();
let mut original = BTreeSet::new();
original.insert(1);
original.insert(2);
original.insert(3);
- let trait_obj: Box<dyn Serializer> = Box::new(original.clone());
- let serialized = fory.serialize(&trait_obj).unwrap();
-
+ let serialized = fory.serialize(&original).unwrap();
let deserialized_concrete: BTreeSet<i32> =
fory.deserialize(&serialized).unwrap();
assert_eq!(deserialized_concrete.len(), 3);
diff --git a/rust/tests/tests/test_enum.rs b/rust/tests/tests/test_enum.rs
index 566baff9d..5398d68a7 100644
--- a/rust/tests/tests/test_enum.rs
+++ b/rust/tests/tests/test_enum.rs
@@ -223,7 +223,7 @@ fn union_compatible_enum_xlang_format() {
}
#[test]
-fn unknown_case_reads_threadsafe_generated_payload() {
+fn unknown_case_reads_send_sync_payload() {
use fory_core::ArcWeak;
use std::sync::{Arc, Mutex};
diff --git a/rust/tests/tests/test_lifecycle_guard.rs
b/rust/tests/tests/test_lifecycle_guard.rs
index 56e8eb64b..7c3c9f80a 100644
--- a/rust/tests/tests/test_lifecycle_guard.rs
+++ b/rust/tests/tests/test_lifecycle_guard.rs
@@ -195,19 +195,6 @@ fn
test_register_serializer_by_name_with_namespace_after_serialize_fails() {
assert!(matches!(err, Error::NotAllowed(_)));
}
-/// Ensures `register_generic_trait()` is forbidden after snapshot init.
-#[test]
-fn test_register_generic_trait_after_serialize_fails() {
- let mut fory = Fory::builder().xlang(false).build();
- fory.register::<Point>(100).unwrap();
- let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap();
-
- let err = fory
- .register_generic_trait::<Vec<i32>>()
- .expect_err("register_generic_trait after serialize should fail");
- assert!(matches!(err, Error::NotAllowed(_)));
-}
-
/// Ensures `register_union()` is forbidden after snapshot init.
#[test]
fn test_register_union_after_serialize_fails() {
diff --git a/rust/tests/tests/test_send_sync.rs
b/rust/tests/tests/test_send_sync.rs
new file mode 100644
index 000000000..d7b5a7773
--- /dev/null
+++ b/rust/tests/tests/test_send_sync.rs
@@ -0,0 +1,365 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#![allow(dead_code)]
+
+use fory_core::fory::Fory;
+use fory_core::{
+ read_data, write_data, Config, Error, ForyDefault, ReadContext,
Serializer, TypeResolver,
+ WriteContext,
+};
+use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
+use std::{
+ any::Any,
+ collections::{HashMap, HashSet, LinkedList},
+ fmt::Debug,
+ rc::Rc,
+ sync::Arc,
+};
+
+fn assert_arc_any_roundtrip<T>(fory: &Fory, value: T)
+where
+ T: 'static + Clone + Debug + PartialEq + Send + Sync,
+{
+ let wrapped: Arc<dyn Any + Send + Sync> = Arc::new(value.clone());
+ let bytes = fory.serialize(&wrapped).unwrap();
+ let decoded: Arc<dyn Any + Send + Sync> =
fory.deserialize(&bytes).unwrap();
+ assert_eq!(decoded.downcast_ref::<T>().unwrap(), &value);
+}
+
+fn assert_arc_any_unsupported<T>(fory: &Fory, value: T)
+where
+ T: 'static + Send + Sync,
+{
+ let wrapped: Arc<dyn Any + Send + Sync> = Arc::new(value);
+ let err = match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Arc<dyn Any + Send + Sync>, _> =
fory.deserialize(&bytes);
+ match result {
+ Ok(_) => panic!("expected direct generic container payload to
be unsupported"),
+ Err(err) => err,
+ }
+ }
+ Err(err) => err,
+ };
+ let message = err.to_string();
+ assert!(
+ message.contains("top-level erased Any")
+ || message.contains("Erased Any payloads require")
+ || message.contains("cannot be represented as Arc<dyn Any + Send +
Sync>"),
+ "unexpected error: {err}"
+ );
+}
+
+fn assert_send_sync_reader_unsupported<T>()
+where
+ T: Serializer + ForyDefault,
+{
+ let mut context = ReadContext::new(TypeResolver::default(),
Config::default());
+ let result = T::fory_read_data_as_send_sync_any(&mut context);
+ let err = match result {
+ Ok(_) => panic!("expected send-sync Any reader to be unsupported"),
+ Err(err) => err,
+ };
+ let message = err.to_string();
+ assert!(
+ message.contains("cannot be represented as Arc<dyn Any + Send +
Sync>"),
+ "unexpected error: {err}"
+ );
+}
+
+#[test]
+fn test_builtin_send_sync_arc_any_reads() {
+ let fory = Fory::builder().xlang(false).build();
+
+ assert_arc_any_roundtrip(&fory, 42_i32);
+ assert_arc_any_roundtrip(&fory, true);
+ assert_arc_any_roundtrip(&fory, "thread-safe".to_string());
+}
+
+#[test]
+fn test_derived_send_sync_arc_any_read() {
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct Value {
+ name: String,
+ count: i32,
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register::<Value>(900).unwrap();
+
+ assert_arc_any_roundtrip(
+ &fory,
+ Value {
+ name: "derived".to_string(),
+ count: 7,
+ },
+ );
+}
+
+#[test]
+fn compatible_struct_arc_any_read() {
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct ValueV1 {
+ #[fory(id = 0)]
+ name: String,
+ #[fory(id = 1)]
+ count: i32,
+ }
+
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct ValueV2 {
+ #[fory(id = 0)]
+ name: String,
+ #[fory(id = 1)]
+ count: i32,
+ #[fory(id = 2)]
+ label: String,
+ }
+
+ let mut writer = Fory::builder().xlang(false).compatible(true).build();
+ writer.register::<ValueV1>(909).unwrap();
+
+ let mut reader = Fory::builder().xlang(false).compatible(true).build();
+ reader.register::<ValueV2>(909).unwrap();
+
+ let value: Arc<dyn Any + Send + Sync> = Arc::new(ValueV1 {
+ name: "compatible".to_string(),
+ count: 11,
+ });
+ let bytes = writer.serialize(&value).unwrap();
+ let decoded: Arc<dyn Any + Send + Sync> =
reader.deserialize(&bytes).unwrap();
+ let value = decoded.downcast_ref::<ValueV2>().unwrap();
+
+ assert_eq!(value.name, "compatible");
+ assert_eq!(value.count, 11);
+ assert_eq!(value.label, String::default());
+}
+
+#[test]
+fn wrapped_container_arc_any_read() {
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct IntList {
+ values: Vec<i32>,
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register::<IntList>(901).unwrap();
+
+ assert_arc_any_roundtrip(
+ &fory,
+ IntList {
+ values: vec![1, 2, 3],
+ },
+ );
+}
+
+#[test]
+fn generic_containers_rejected_arc_any() {
+ let fory = Fory::builder().xlang(false).build();
+
+ assert_arc_any_unsupported(&fory, vec![1_i32, 2, 3]);
+ assert_arc_any_unsupported(&fory, LinkedList::from([1_i32, 2, 3]));
+ assert_arc_any_unsupported(&fory, HashSet::from([1_i32, 2, 3]));
+ assert_arc_any_unsupported(
+ &fory,
+ HashMap::from([("one".to_string(), 1_i32), ("two".to_string(), 2)]),
+ );
+}
+
+#[test]
+fn test_auto_send_sync_struct() {
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct Value {
+ name: String,
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register::<Value>(902).unwrap();
+
+ assert_arc_any_roundtrip(
+ &fory,
+ Value {
+ name: "auto".to_string(),
+ },
+ );
+}
+
+#[test]
+fn non_send_sync_carrier_reader_unsupported() {
+ assert_send_sync_reader_unsupported::<Rc<dyn Any>>();
+}
+
+#[test]
+fn manual_serializer_arc_any_read() {
+ #[derive(Clone, Debug, PartialEq)]
+ struct ManualValue {
+ id: i32,
+ name: String,
+ }
+
+ impl ForyDefault for ManualValue {
+ fn fory_default() -> Self {
+ Self {
+ id: 0,
+ name: String::new(),
+ }
+ }
+ }
+
+ impl Serializer for ManualValue {
+ fn fory_write_data(&self, context: &mut WriteContext) -> Result<(),
Error> {
+ write_data(&self.id, context)?;
+ write_data(&self.name, context)
+ }
+
+ fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
+ Ok(Self {
+ id: read_data(context)?,
+ name: read_data(context)?,
+ })
+ }
+
+ fn fory_read_data_as_send_sync_any(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn Any + Send + Sync>, Error> {
+ Ok(fory_core::serializer::box_send_sync(Self::fory_read_data(
+ context,
+ )?))
+ }
+
+ fn fory_type_id_dyn(
+ &self,
+ type_resolver: &TypeResolver,
+ ) -> Result<fory_core::TypeId, Error> {
+ Self::fory_get_type_id(type_resolver)
+ }
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register_serializer::<ManualValue>(910).unwrap();
+
+ assert_arc_any_roundtrip(
+ &fory,
+ ManualValue {
+ id: 7,
+ name: "manual".to_string(),
+ },
+ );
+}
+
+#[test]
+fn test_nested_custom_default() {
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct Leaf {
+ name: String,
+ }
+
+ #[derive(ForyStruct, Clone, Debug, PartialEq)]
+ struct Value {
+ leaf: Leaf,
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register::<Leaf>(902).unwrap();
+ fory.register::<Value>(903).unwrap();
+
+ assert_arc_any_roundtrip(
+ &fory,
+ Value {
+ leaf: Leaf {
+ name: "nested".to_string(),
+ },
+ },
+ );
+}
+
+#[test]
+fn test_known_non_send_sync_struct() {
+ #[derive(ForyStruct)]
+ struct Value {
+ name: Rc<String>,
+ }
+
+ assert_send_sync_reader_unsupported::<Value>();
+}
+
+#[test]
+fn test_send_sync_union() {
+ #[derive(ForyUnion, Clone, Debug, PartialEq)]
+ enum Event {
+ #[fory(unknown)]
+ Unknown(fory_core::UnknownCase),
+ #[fory(id = 0, default)]
+ Value(String),
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register_union::<Event>(905).unwrap();
+
+ assert_arc_any_roundtrip(&fory, Event::Value("union".to_string()));
+}
+
+#[test]
+fn test_send_sync_enum() {
+ #[derive(ForyEnum, Clone, Debug, Default, PartialEq)]
+ enum Status {
+ #[default]
+ Active,
+ Inactive,
+ }
+
+ let mut fory = Fory::builder().xlang(false).build();
+ fory.register::<Status>(906).unwrap();
+
+ assert_arc_any_roundtrip(&fory, Status::Inactive);
+}
+
+#[test]
+fn compatible_union_arc_any_read() {
+ #[derive(ForyUnion, Clone, Debug, PartialEq)]
+ enum Event {
+ #[fory(unknown)]
+ Unknown(fory_core::UnknownCase),
+ #[fory(id = 0, default)]
+ Value(String),
+ }
+
+ let mut fory = Fory::builder().xlang(false).compatible(true).build();
+ fory.register_union::<Event>(907).unwrap();
+
+ assert_arc_any_roundtrip(&fory, Event::Value("compatible".to_string()));
+}
+
+#[test]
+fn compatible_enum_arc_any_read() {
+ #[derive(ForyEnum, Clone, Debug, Default, PartialEq)]
+ enum Status {
+ #[default]
+ Active,
+ Inactive,
+ }
+
+ let mut fory = Fory::builder().xlang(false).compatible(true).build();
+ fory.register::<Status>(908).unwrap();
+
+ assert_arc_any_roundtrip(&fory, Status::Inactive);
+}
diff --git a/rust/tests/tests/test_trait_object.rs
b/rust/tests/tests/test_trait_object.rs
index 9544c4bdb..923ae6266 100644
--- a/rust/tests/tests/test_trait_object.rs
+++ b/rust/tests/tests/test_trait_object.rs
@@ -17,7 +17,9 @@
use fory_core::fory::Fory;
use fory_core::register_trait_type;
+use fory_core::resolver::RefFlag;
use fory_core::serializer::Serializer;
+use fory_core::TypeId;
use fory_derive::ForyStruct;
use std::collections::{HashMap, HashSet};
@@ -25,6 +27,16 @@ fn fory_compatible() -> Fory {
Fory::builder().xlang(false).compatible(true).build()
}
+fn assert_erased_serializer_container_error(err: fory_core::Error) {
+ let message = err.to_string();
+ assert!(
+ message.contains("Type info for internal type not found")
+ || message.contains("not found in type_info registry")
+ || message.contains("ID harness not found"),
+ "unexpected error: {message}"
+ );
+}
+
#[test]
fn test_multiple_types_in_sequence() {
let fory = fory_compatible();
@@ -74,41 +86,64 @@ fn test_option_some_roundtrip() {
}
#[test]
-fn test_hashmap_roundtrip() {
+fn trait_object_map_set_payloads_rejected() {
let fory = fory_compatible();
- let mut original = HashMap::new();
- original.insert(String::from("one"), 1);
- original.insert(String::from("two"), 2);
- original.insert(String::from("three"), 3);
- let trait_obj: Box<dyn Serializer> = Box::new(original.clone());
- let serialized = fory.serialize(&trait_obj).unwrap();
-
- let deserialized_concrete: HashMap<String, i32> =
fory.deserialize(&serialized).unwrap();
-
- assert_eq!(deserialized_concrete.len(), 3);
- assert_eq!(deserialized_concrete.get("one"), Some(&1));
- assert_eq!(deserialized_concrete.get("two"), Some(&2));
- assert_eq!(deserialized_concrete.get("three"), Some(&3));
+ let list: Box<dyn Serializer> = Box::new(vec!["one".to_string(),
"two".to_string()]);
+
assert_erased_serializer_container_error(fory.serialize(&list).unwrap_err());
+
+ let representative_map: Box<dyn Serializer> = Box::new(HashMap::from([
+ ("one".to_string(), 1_i32),
+ ("two".to_string(), 2),
+ ]));
+
assert_erased_serializer_container_error(fory.serialize(&representative_map).unwrap_err());
+
+ let other_map: Box<dyn Serializer> = Box::new(HashMap::from([
+ ("one".to_string(), "first".to_string()),
+ ("two".to_string(), "second".to_string()),
+ ]));
+
assert_erased_serializer_container_error(fory.serialize(&other_map).unwrap_err());
+
+ let representative_set: Box<dyn Serializer> =
Box::new(HashSet::from([1_i32, 2, 3]));
+
assert_erased_serializer_container_error(fory.serialize(&representative_set).unwrap_err());
+
+ let other_set: Box<dyn Serializer> = Box::new(HashSet::from([
+ "one".to_string(),
+ "two".to_string(),
+ "three".to_string(),
+ ]));
+
assert_erased_serializer_container_error(fory.serialize(&other_set).unwrap_err());
}
#[test]
-fn test_hashset_roundtrip() {
+fn trait_object_map_values_reject_containers() {
let fory = fory_compatible();
- let mut original = HashSet::new();
- original.insert(1);
- original.insert(2);
- original.insert(3);
- let trait_obj: Box<dyn Serializer> = Box::new(original.clone());
- let serialized = fory.serialize(&trait_obj).unwrap();
+ let mut values: HashMap<String, Box<dyn Serializer>> = HashMap::new();
+ values.insert(
+ "map".to_string(),
+ Box::new(HashMap::from([("one".to_string(), 1_i32)])),
+ );
+
assert_erased_serializer_container_error(fory.serialize(&values).unwrap_err());
+
+ let mut values: HashMap<String, Box<dyn Serializer>> = HashMap::new();
+ values.insert("set".to_string(), Box::new(HashSet::from([1_i32, 2, 3])));
+
assert_erased_serializer_container_error(fory.serialize(&values).unwrap_err());
+}
- let deserialized_concrete: HashSet<i32> =
fory.deserialize(&serialized).unwrap();
+#[test]
+fn trait_object_container_type_ids_rejected_on_read() {
+ let fory = fory_compatible();
- assert_eq!(deserialized_concrete.len(), 3);
- assert!(deserialized_concrete.contains(&1));
- assert!(deserialized_concrete.contains(&2));
- assert!(deserialized_concrete.contains(&3));
+ for type_id in [TypeId::LIST, TypeId::SET, TypeId::MAP] {
+ let bytes = vec![0, RefFlag::NotNullValue as i8 as u8, type_id as u8];
+ let result: Result<Box<dyn Serializer>, _> = fory.deserialize(&bytes);
+ let err = match result {
+ Ok(_) => panic!("expected erased container type id to fail"),
+ Err(err) => err,
+ };
+ assert_erased_serializer_container_error(err);
+ }
}
#[test]
diff --git a/rust/tests/tests/test_unsigned.rs
b/rust/tests/tests/test_unsigned.rs
index 4f4be267a..dd75467b5 100644
--- a/rust/tests/tests/test_unsigned.rs
+++ b/rust/tests/tests/test_unsigned.rs
@@ -19,8 +19,65 @@ mod test_helpers;
use fory_core::fory::Fory;
use fory_derive::ForyStruct;
+use std::any::Any;
+use std::rc::Rc;
+use std::sync::Arc;
use test_helpers::{test_arc_any, test_box_any, test_rc_any, test_roundtrip};
+fn assert_erased_container_error(message: String) {
+ assert!(
+ message.contains("top-level erased Any")
+ || message.contains("Erased Any payloads require")
+ || message.contains("cannot be represented as Arc<dyn Any + Send +
Sync>"),
+ "unexpected error: {message}"
+ );
+}
+
+fn test_box_any_unsupported<T>(fory: &Fory, value: T)
+where
+ T: 'static,
+{
+ let wrapped: Box<dyn Any> = Box::new(value);
+ match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Box<dyn Any>, _> = fory.deserialize(&bytes);
+ let err = result.expect_err("expected Box<dyn Any> container read
to fail");
+ assert_erased_container_error(err.to_string());
+ }
+ Err(err) => assert_erased_container_error(err.to_string()),
+ }
+}
+
+fn test_rc_any_unsupported<T>(fory: &Fory, value: T)
+where
+ T: 'static,
+{
+ let wrapped: Rc<dyn Any> = Rc::new(value);
+ match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Rc<dyn Any>, _> = fory.deserialize(&bytes);
+ let err = result.expect_err("expected Rc<dyn Any> container read
to fail");
+ assert_erased_container_error(err.to_string());
+ }
+ Err(err) => assert_erased_container_error(err.to_string()),
+ }
+}
+
+fn test_arc_any_unsupported<T>(fory: &Fory, value: T)
+where
+ T: 'static + Send + Sync,
+{
+ let wrapped: Arc<dyn Any + Send + Sync> = Arc::new(value);
+ match fory.serialize(&wrapped) {
+ Ok(bytes) => {
+ let result: Result<Arc<dyn Any + Send + Sync>, _> =
fory.deserialize(&bytes);
+ let err = result.expect_err("expected Arc<dyn Any> container read
to fail");
+ assert_erased_container_error(err.to_string());
+ }
+ Err(err) => assert_erased_container_error(err.to_string()),
+ };
+}
+
#[test]
fn test_unsigned_numbers() {
let fory = Fory::builder().xlang(false).build();
@@ -443,27 +500,25 @@ fn test_unsigned_with_smart_pointers() {
test_arc_any(&fory, usize::MAX);
test_arc_any(&fory, u128::MAX);
- // Test Box<dyn Any> with unsigned arrays
- test_box_any(&fory, vec![0u8, 127, u8::MAX]);
- test_box_any(&fory, vec![0u16, 1000, u16::MAX]);
- test_box_any(&fory, vec![0u32, 1000000, u32::MAX]);
- test_box_any(&fory, vec![0u64, 1000000000000, u64::MAX]);
- test_box_any(&fory, vec![0usize, 1000000000000, usize::MAX]);
- test_box_any(&fory, vec![0u128, 1000000000000, u128::MAX]);
-
- // Test Rc<dyn Any> with unsigned arrays
- test_rc_any(&fory, vec![0u8, 127, u8::MAX]);
- test_rc_any(&fory, vec![100u16, 200, 300, u16::MAX]);
- test_rc_any(&fory, vec![1000u32, 2000, 3000, u32::MAX]);
- test_rc_any(&fory, vec![0u64, 1000000000000, u64::MAX]);
- test_rc_any(&fory, vec![0usize, 1000000000000, usize::MAX]);
- test_rc_any(&fory, vec![0u128, 1000000000000, u128::MAX]);
-
- // Test Arc<dyn Any + Send + Sync> with unsigned arrays
- test_arc_any(&fory, vec![0u8, 127, u8::MAX]);
- test_arc_any(&fory, vec![100u16, 200, 300, u16::MAX]);
- test_arc_any(&fory, vec![999u32, 888, 777, u32::MAX]);
- test_arc_any(&fory, vec![123u64, 456789, 987654321, u64::MAX]);
- test_arc_any(&fory, vec![123usize, 456789, 987654321, usize::MAX]);
- test_arc_any(&fory, vec![0u128, 1000000000000, u128::MAX]);
+ // Direct vectors are not supported as top-level erased Any payloads.
+ test_box_any_unsupported(&fory, vec![0u8, 127, u8::MAX]);
+ test_box_any_unsupported(&fory, vec![0u16, 1000, u16::MAX]);
+ test_box_any_unsupported(&fory, vec![0u32, 1000000, u32::MAX]);
+ test_box_any_unsupported(&fory, vec![0u64, 1000000000000, u64::MAX]);
+ test_box_any_unsupported(&fory, vec![0usize, 1000000000000, usize::MAX]);
+ test_box_any_unsupported(&fory, vec![0u128, 1000000000000, u128::MAX]);
+
+ test_rc_any_unsupported(&fory, vec![0u8, 127, u8::MAX]);
+ test_rc_any_unsupported(&fory, vec![100u16, 200, 300, u16::MAX]);
+ test_rc_any_unsupported(&fory, vec![1000u32, 2000, 3000, u32::MAX]);
+ test_rc_any_unsupported(&fory, vec![0u64, 1000000000000, u64::MAX]);
+ test_rc_any_unsupported(&fory, vec![0usize, 1000000000000, usize::MAX]);
+ test_rc_any_unsupported(&fory, vec![0u128, 1000000000000, u128::MAX]);
+
+ test_arc_any_unsupported(&fory, vec![0u8, 127, u8::MAX]);
+ test_arc_any_unsupported(&fory, vec![100u16, 200, 300, u16::MAX]);
+ test_arc_any_unsupported(&fory, vec![999u32, 888, 777, u32::MAX]);
+ test_arc_any_unsupported(&fory, vec![123u64, 456789, 987654321, u64::MAX]);
+ test_arc_any_unsupported(&fory, vec![123usize, 456789, 987654321,
usize::MAX]);
+ test_arc_any_unsupported(&fory, vec![0u128, 1000000000000, u128::MAX]);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]