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 e637cf726 feat(rust): support thread safe `Arc<dyn Any + Send + Sync>` 
type (#3736)
e637cf726 is described below

commit e637cf726c23e4f0c38f912bffb52317b2c72c9f
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Jun 4 06:23:01 2026 +0800

    feat(rust): support thread safe `Arc<dyn Any + Send + Sync>` type (#3736)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    Closes #3732
    
    ## 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
---
 compiler/extension/fory_options.proto              |   5 +-
 compiler/fory_compiler/generators/rust.py          |  10 +-
 compiler/fory_compiler/ir/ast.py                   |   7 +
 .../fory_compiler/tests/test_generated_code.py     |  34 ++--
 compiler/fory_compiler/tests/test_weak_ref.py      |  10 +-
 docs/compiler/flatbuffers-idl.md                   |  20 +-
 docs/compiler/protobuf-idl.md                      |  25 +--
 docs/compiler/schema-idl.md                        |  67 +++----
 docs/guide/rust/configuration.md                   |   2 +-
 docs/guide/rust/native-serialization.md            |   2 +-
 docs/guide/rust/polymorphism.md                    |  19 +-
 docs/guide/rust/schema-evolution.md                |   4 +-
 .../idl_tests/rust/tests/idl_roundtrip.rs          |  90 ++++-----
 rust/README.md                                     |   2 +-
 rust/fory-core/src/resolver/type_resolver.rs       | 174 ++++++++++------
 rust/fory-core/src/serializer/any.rs               |  84 +++++---
 rust/fory-core/src/serializer/arc.rs               |  20 ++
 rust/fory-core/src/serializer/codec.rs             |   2 +-
 rust/fory-core/src/serializer/core.rs              | 218 +++++++++++++++++++++
 rust/fory-core/src/serializer/mod.rs               |   6 +-
 rust/fory-core/src/serializer/trait_object.rs      |   3 +-
 rust/fory-core/src/serializer/unknown_case.rs      |  30 ++-
 rust/fory-core/src/types/unknown_case.rs           |  25 ++-
 rust/fory-derive/src/object/field_codec.rs         |  74 ++++---
 rust/fory-derive/src/object/serializer.rs          |  87 ++++++++
 rust/fory-derive/src/object/util.rs                | 158 ++++++++++++++-
 rust/fory/src/lib.rs                               |  17 +-
 rust/tests/tests/test_any.rs                       |  51 +++--
 rust/tests/tests/test_enum.rs                      | 103 ++++++++++
 rust/tests/tests/test_helpers.rs                   |   8 +-
 rust/tests/tests/test_ref_resolver.rs              |  15 ++
 rust/tests/tests/test_unsigned.rs                  |   4 +-
 32 files changed, 1082 insertions(+), 294 deletions(-)

diff --git a/compiler/extension/fory_options.proto 
b/compiler/extension/fory_options.proto
index 62ae96802..9d1cb8b7f 100644
--- a/compiler/extension/fory_options.proto
+++ b/compiler/extension/fory_options.proto
@@ -179,10 +179,11 @@ message ForyFieldOptions {
   optional bool weak_ref = 4;
 
   // Generate thread-safe Rust pointer carriers for ref fields.
-  // When true, Rust codegen uses Arc/ArcWeak instead of Rc/RcWeak.
+  // Rust codegen uses Arc/ArcWeak by default. Set this to false to generate
+  // Rc/RcWeak for ref fields that must stay single-threaded.
   // This does not change the wire format and does not make the referenced
   // value itself thread-safe.
-  // Default: false
+  // Default: true
   optional bool thread_safe_pointer = 5;
 }
 
diff --git a/compiler/fory_compiler/generators/rust.py 
b/compiler/fory_compiler/generators/rust.py
index 652a9cd6e..a72642e66 100644
--- a/compiler/fory_compiler/generators/rust.py
+++ b/compiler/fory_compiler/generators/rust.py
@@ -34,6 +34,7 @@ from fory_compiler.ir.ast import (
     ArrayType,
     MapType,
     Schema,
+    thread_safe_pointer_enabled,
 )
 from fory_compiler.ir.types import PrimitiveKind
 
@@ -43,6 +44,7 @@ class RustGenerator(BaseGenerator):
 
     language_name = "rust"
     file_extension = ".rs"
+    RUST_ANY_TYPE = "::std::sync::Arc<dyn ::std::any::Any + Send + Sync>"
 
     # Mapping from FDL primitive types to Rust types
     PRIMITIVE_MAP = {
@@ -62,7 +64,7 @@ class RustGenerator(BaseGenerator):
         PrimitiveKind.STRING: "::std::string::String",
         PrimitiveKind.BYTES: "::std::vec::Vec<u8>",
         PrimitiveKind.DECIMAL: "::fory::Decimal",
-        PrimitiveKind.ANY: "::std::boxed::Box<dyn ::std::any::Any>",
+        PrimitiveKind.ANY: RUST_ANY_TYPE,
     }
 
     FORY_TEMPORAL_MAP = {
@@ -1012,12 +1014,12 @@ class RustGenerator(BaseGenerator):
         element_optional: bool = False,
         element_ref: bool = False,
         parent_stack: Optional[List[Message]] = None,
-        pointer_type: str = "::std::rc::Rc",
+        pointer_type: str = "::std::sync::Arc",
     ) -> str:
         """Generate Rust type string."""
         if isinstance(field_type, PrimitiveType):
             if field_type.kind == PrimitiveKind.ANY:
-                return "::std::boxed::Box<dyn ::std::any::Any>"
+                return self.RUST_ANY_TYPE
             base_type = self.primitive_type_name(field_type.kind)
             if nullable:
                 return f"::std::option::Option<{base_type}>"
@@ -1152,7 +1154,7 @@ class RustGenerator(BaseGenerator):
 
     def get_pointer_type(self, ref_options: dict, weak_ref: bool = False) -> 
str:
         """Determine pointer type for ref tracking based on field options."""
-        if ref_options.get("thread_safe_pointer") is True:
+        if thread_safe_pointer_enabled(ref_options):
             return "::fory::ArcWeak" if weak_ref else "::std::sync::Arc"
         return "::fory::RcWeak" if weak_ref else "::std::rc::Rc"
 
diff --git a/compiler/fory_compiler/ir/ast.py b/compiler/fory_compiler/ir/ast.py
index f2718e84f..24360fa01 100644
--- a/compiler/fory_compiler/ir/ast.py
+++ b/compiler/fory_compiler/ir/ast.py
@@ -22,6 +22,13 @@ from typing import List, Optional, Union as TypingUnion
 
 from fory_compiler.ir.types import PrimitiveKind
 
+THREAD_SAFE_POINTER_DEFAULT = True
+
+
+def thread_safe_pointer_enabled(ref_options: dict) -> bool:
+    """Return the effective Rust pointer-carrier default for ref options."""
+    return ref_options.get("thread_safe_pointer", THREAD_SAFE_POINTER_DEFAULT) 
is True
+
 
 @dataclass(frozen=True)
 class SourceLocation:
diff --git a/compiler/fory_compiler/tests/test_generated_code.py 
b/compiler/fory_compiler/tests/test_generated_code.py
index 62f6350f4..f15eb3971 100644
--- a/compiler/fory_compiler/tests/test_generated_code.py
+++ b/compiler/fory_compiler/tests/test_generated_code.py
@@ -216,8 +216,8 @@ def 
test_rust_nested_container_ref_uses_correct_pointer_type():
             }
 
             message Request {
-                list<list<ref(thread_safe=true) Node>> groups = 1;
-                map<string, map<string, ref(thread_safe=true) Node>> nodes = 2;
+                list<list<ref Node>> groups = 1;
+                map<string, map<string, ref Node>> nodes = 2;
             }
             """
         )
@@ -497,7 +497,7 @@ def test_generated_code_map_types_equivalent():
     assert "SharedWeak<MapValue>" in cpp_output
 
 
-def test_rust_generated_ref_pointer_default_and_thread_safe_option():
+def test_rust_generated_ref_pointer_default_and_opt_out():
     schema = parse_fdl(
         dedent(
             """
@@ -509,25 +509,24 @@ def 
test_rust_generated_ref_pointer_default_and_thread_safe_option():
 
             message Holder {
                 ref Node default_ref = 1;
-                ref(thread_safe=true) Node thread_safe_ref = 2;
+                ref(thread_safe=false) Node rc_ref = 2;
                 ref(weak=true) Node default_weak_ref = 3;
-                ref(weak=true, thread_safe=true) Node thread_safe_weak_ref = 4;
+                ref(weak=true, thread_safe=false) Node rc_weak_ref = 4;
                 list<ref Node> default_ref_list = 5;
-                list<ref(thread_safe=true) Node> thread_safe_ref_list = 6;
+                list<ref(thread_safe=false) Node> rc_ref_list = 6;
             }
             """
         )
     )
     rust_output = render_files(generate_files(schema, RustGenerator))
-    assert "pub default_ref: ::std::rc::Rc<Node>," in rust_output
-    assert "pub thread_safe_ref: ::std::sync::Arc<Node>," in rust_output
-    assert "pub default_weak_ref: ::fory::RcWeak<Node>," in rust_output
-    assert "pub thread_safe_weak_ref: ::fory::ArcWeak<Node>," in rust_output
-    assert "pub default_ref_list: ::std::vec::Vec<::std::rc::Rc<Node>>," in 
rust_output
+    assert "pub default_ref: ::std::sync::Arc<Node>," in rust_output
+    assert "pub rc_ref: ::std::rc::Rc<Node>," in rust_output
+    assert "pub default_weak_ref: ::fory::ArcWeak<Node>," in rust_output
+    assert "pub rc_weak_ref: ::fory::RcWeak<Node>," in rust_output
     assert (
-        "pub thread_safe_ref_list: ::std::vec::Vec<::std::sync::Arc<Node>>,"
-        in rust_output
+        "pub default_ref_list: ::std::vec::Vec<::std::sync::Arc<Node>>," in 
rust_output
     )
+    assert "pub rc_ref_list: ::std::vec::Vec<::std::rc::Rc<Node>>," in 
rust_output
 
 
 def test_generated_code_nested_messages_equivalent():
@@ -779,7 +778,7 @@ def test_generated_code_tree_ref_options_equivalent():
     assert_all_languages_equal(schemas)
 
     rust_output = render_files(generate_files(schemas["fdl"], RustGenerator))
-    assert "RcWeak<TreeNode>" in rust_output
+    assert "ArcWeak<TreeNode>" in rust_output
     assert "#[derive(::fory::ForyStruct, Clone, PartialEq, Eq, Default)]" in 
rust_output
 
     cpp_output = render_files(generate_files(schemas["fdl"], CppGenerator))
@@ -1153,8 +1152,11 @@ def test_rust_generated_code_uses_absolute_paths():
         "pub labels: ::std::collections::HashMap<::std::string::String, 
::std::string::String>,"
         in rust_output
     )
-    assert "pub payload: ::std::boxed::Box<dyn ::std::any::Any>," in 
rust_output
-    assert "pub parent: ::fory::RcWeak<String>," in rust_output
+    assert (
+        "pub payload: ::std::sync::Arc<dyn ::std::any::Any + Send + Sync>,"
+        in rust_output
+    )
+    assert "pub parent: ::fory::ArcWeak<String>," in rust_output
     assert "pub fn register_types(fory: &mut ::fory::Fory)" in rust_output
     assert "static FORY: ::std::sync::OnceLock<::fory::Fory>" in rust_output
 
diff --git a/compiler/fory_compiler/tests/test_weak_ref.py 
b/compiler/fory_compiler/tests/test_weak_ref.py
index 35fb1f64c..a90cb8382 100644
--- a/compiler/fory_compiler/tests/test_weak_ref.py
+++ b/compiler/fory_compiler/tests/test_weak_ref.py
@@ -122,7 +122,7 @@ def test_weak_ref_requires_repeated_ref():
     )
 
 
-def test_list_and_map_ref_options_with_thread_safe():
+def test_list_and_map_ref_options_preserve_explicit_opt_out():
     source = """
     message Foo {
         int32 id = 1;
@@ -134,8 +134,8 @@ def test_list_and_map_ref_options_with_thread_safe():
 
     message Holder {
         list<ref Foo> foos = 1;
-        list<ref(weak=true, thread_safe=true) Bar> bars = 2;
-        map<Foo, ref(weak=true, thread_safe=true) Bar> bar_map = 3;
+        list<ref(weak=true, thread_safe=false) Bar> bars = 2;
+        map<Foo, ref(weak=true, thread_safe=false) Bar> bar_map = 3;
     }
     """
     schema = parse_schema(source)
@@ -152,8 +152,8 @@ def test_list_and_map_ref_options_with_thread_safe():
 
     assert bars.element_ref is True
     assert bars.element_ref_options.get("weak_ref") is True
-    assert bars.element_ref_options.get("thread_safe_pointer") is True
+    assert bars.element_ref_options.get("thread_safe_pointer") is False
 
     assert bar_map.field_type.value_ref is True
     assert bar_map.field_type.value_ref_options.get("weak_ref") is True
-    assert bar_map.field_type.value_ref_options.get("thread_safe_pointer") is 
True
+    assert bar_map.field_type.value_ref_options.get("thread_safe_pointer") is 
False
diff --git a/docs/compiler/flatbuffers-idl.md b/docs/compiler/flatbuffers-idl.md
index 71a249f03..f7aa298c2 100644
--- a/docs/compiler/flatbuffers-idl.md
+++ b/docs/compiler/flatbuffers-idl.md
@@ -156,20 +156,20 @@ FlatBuffers metadata attributes use `key:value`. For 
Fory-specific options, use
 
 ### Supported Field Attributes
 
-| FlatBuffers Attribute           | Effect in Fory                             
                                      |
-| ------------------------------- | 
--------------------------------------------------------------------------------
 |
-| `fory_ref:true`                 | Enable reference tracking for the field    
                                      |
-| `fory_nullable:true`            | Mark field optional/nullable               
                                      |
-| `fory_weak_ref:true`            | Enable weak reference semantics and 
implies `ref`                                |
-| `fory_thread_safe_pointer:true` | For ref fields, select Rust 
`Arc`/`ArcWeak` instead of the default `Rc`/`RcWeak` |
+| FlatBuffers Attribute            | Effect in Fory                            
                                       |
+| -------------------------------- | 
--------------------------------------------------------------------------------
 |
+| `fory_ref:true`                  | Enable reference tracking for the field   
                                       |
+| `fory_nullable:true`             | Mark field optional/nullable              
                                       |
+| `fory_weak_ref:true`             | Enable weak reference semantics and 
implies `ref`                                |
+| `fory_thread_safe_pointer:false` | For ref fields, select Rust `Rc`/`RcWeak` 
instead of the default `Arc`/`ArcWeak` |
 
 Semantics:
 
 - `fory_weak_ref:true` implies `ref`.
-- `fory_thread_safe_pointer` defaults to `false`, only takes effect when the 
field
+- `fory_thread_safe_pointer` defaults to `true`, only takes effect when the 
field
   is ref-tracked, and does not change the wire format.
-- In Rust codegen, `fory_weak_ref:true` uses `RcWeak` by default and switches 
to
-  `ArcWeak` only when `fory_thread_safe_pointer:true` is also set.
+- In Rust codegen, `fory_weak_ref:true` uses `ArcWeak` by default and switches 
to
+  `RcWeak` only when `fory_thread_safe_pointer:false` is set.
 - For list fields, `fory_ref:true` applies to list elements.
 
 Example:
@@ -178,7 +178,7 @@ Example:
 table Node {
   parent: Node (fory_weak_ref: true);
   children: [Node] (fory_ref: true);
-  cached: Node (fory_ref: true, fory_thread_safe_pointer: true);
+  local: Node (fory_ref: true, fory_thread_safe_pointer: false);
 }
 ```
 
diff --git a/docs/compiler/protobuf-idl.md b/docs/compiler/protobuf-idl.md
index 3a13fb8ff..59f4c63a7 100644
--- a/docs/compiler/protobuf-idl.md
+++ b/docs/compiler/protobuf-idl.md
@@ -232,14 +232,14 @@ message TreeNode {
 
 ### Field-Level Options
 
-| Option                       | Type   | Description                          
                                       |
-| ---------------------------- | ------ | 
--------------------------------------------------------------------------- |
-| `(fory).ref`                 | bool   | Enable reference tracking for this 
field                                    |
-| `(fory).nullable`            | bool   | Treat field as nullable (`optional`) 
                                       |
-| `(fory).weak_ref`            | bool   | Generate weak pointer semantics 
(C++/Rust codegen)                          |
-| `(fory).thread_safe_pointer` | bool   | Use Rust `Arc`/`ArcWeak` for ref 
fields; default `false` uses `Rc`/`RcWeak` |
-| `(fory).deprecated`          | bool   | Mark field as deprecated             
                                       |
-| `(fory).type`                | string | Primitive override for tagged 64-bit 
integer encoding                       |
+| Option                       | Type   | Description                          
                                                                |
+| ---------------------------- | ------ | 
----------------------------------------------------------------------------------------------------
 |
+| `(fory).ref`                 | bool   | Enable reference tracking for this 
field                                                             |
+| `(fory).nullable`            | bool   | Treat field as nullable (`optional`) 
                                                                |
+| `(fory).weak_ref`            | bool   | Generate weak pointer semantics 
(C++/Rust codegen)                                                   |
+| `(fory).thread_safe_pointer` | bool   | Rust ref carrier selection; default 
`true` uses `Arc`/`ArcWeak`, explicit `false` uses `Rc`/`RcWeak` |
+| `(fory).deprecated`          | bool   | Mark field as deprecated             
                                                                |
+| `(fory).type`                | string | Primitive override for tagged 64-bit 
integer encoding                                                |
 
 Reference option behavior:
 
@@ -247,19 +247,20 @@ Reference option behavior:
 - For `repeated` fields, `(fory).ref = true` applies to list elements.
 - For `map<K, V>` fields, `(fory).ref = true` applies to map values.
 - `weak_ref` and `thread_safe_pointer` are codegen hints for C++/Rust.
-- `thread_safe_pointer` defaults to `false`; it changes only the generated Rust
+- `thread_safe_pointer` defaults to `true`; it changes only the generated Rust
   pointer carrier and does not change the wire format.
-- In Rust codegen, `(fory).weak_ref = true` uses `RcWeak` by default and 
switches
-  to `ArcWeak` only when `(fory).thread_safe_pointer = true`.
+- In Rust codegen, `(fory).weak_ref = true` uses `ArcWeak` by default and
+  switches to `RcWeak` only when `(fory).thread_safe_pointer = false`.
 
 ### Option Examples by Shape
 
 ```protobuf
 message Graph {
-  Node root = 1 [(fory).ref = true, (fory).thread_safe_pointer = true];
+  Node root = 1 [(fory).ref = true];
   repeated Node nodes = 2 [(fory).ref = true];
   map<string, Node> cache = 3 [(fory).ref = true];
   Node parent = 4 [(fory).weak_ref = true];
+  Node local = 5 [(fory).ref = true, (fory).thread_safe_pointer = false];
 }
 ```
 
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index 5b99fc892..89705883b 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -1052,7 +1052,7 @@ message Node {
 | Java                  | `Node parent`  | `Node parent` with `@Ref`           
       |
 | Python                | `parent: Node` | `parent: Node = 
pyfory.field(ref=True)`    |
 | Go                    | `Parent Node`  | `Parent *Node` with `fory:"ref"`    
       |
-| Rust                  | `parent: Node` | `parent: Rc<Node>`                  
       |
+| Rust                  | `parent: Node` | `parent: Arc<Node>`                 
       |
 | C++                   | `Node parent`  | `std::shared_ptr<Node> parent`      
       |
 | C#                    | `Node parent`  | `Node? parent` with runtime ref 
tracking   |
 | JavaScript/TypeScript | `parent: Node` | `parent: Node` (no ref distinction) 
       |
@@ -1061,21 +1061,21 @@ message Node {
 | Scala                 | `parent: Node` | `@Ref parent: Node`                 
       |
 | Kotlin                | `parent: Node` | `@Ref parent: Node?`                
       |
 
-Rust uses `Rc` and `RcWeak` by default for ref-tracked fields. Use
-`ref(thread_safe=true)` when the generated Rust type must use `Arc` or
-`ArcWeak` for cross-thread shared ownership. This setting is a Rust codegen
-carrier choice; it does not change the wire format or make the referenced value
-itself thread-safe. For protobuf option syntax, see
+Rust uses `Arc` and `ArcWeak` by default for ref-tracked fields. Use
+`ref(thread_safe=false)` when a generated Rust type must use single-threaded
+`Rc` or `RcWeak` carriers. This setting is a Rust codegen carrier choice; it
+does not change the wire format or make the referenced value itself
+thread-safe. For protobuf option syntax, see
 [Protocol Buffers IDL Support](protobuf-idl.md#field-level-options).
 
 Rust pointer carrier mapping:
 
-| Fory IDL                                       | Rust type       |
-| ---------------------------------------------- | --------------- |
-| `ref Node parent`                              | `Rc<Node>`      |
-| `ref(thread_safe=true) Node parent`            | `Arc<Node>`     |
-| `ref(weak=true) Node parent`                   | `RcWeak<Node>`  |
-| `ref(weak=true, thread_safe=true) Node parent` | `ArcWeak<Node>` |
+| Fory IDL                                        | Rust type       |
+| ----------------------------------------------- | --------------- |
+| `ref Node parent`                               | `Arc<Node>`     |
+| `ref(thread_safe=false) Node parent`            | `Rc<Node>`      |
+| `ref(weak=true) Node parent`                    | `ArcWeak<Node>` |
+| `ref(weak=true, thread_safe=false) Node parent` | `RcWeak<Node>`  |
 
 #### `list`
 
@@ -1127,10 +1127,11 @@ accepted as an alias for `list`.
 | ----------------------- | ---------------------------------- | 
--------------------- | ----------------------- | --------------------- | 
----------------------------------------- | 
------------------------------------------------------------- | 
---------------------- |
 | `optional list<string>` | `@Nullable List<String>`           | 
`Optional[List[str]]` | `[]string` + `nullable` | `Option<Vec<String>>` | 
`std::optional<std::vector<std::string>>` | `List<String>?`                     
                          | `Option[List[String]]` |
 | `list<optional string>` | `List<String>` (nullable elements) | 
`List[Optional[str]]` | `[]*string`             | `Vec<Option<String>>` | 
`std::vector<std::optional<std::string>>` | `List<String?>`                     
                          | `List[Option[String]]` |
-| `list<ref User>`        | `List<@Ref User>`                  | `List[User]`  
        | `[]*User` + `ref=false` | `Vec<Rc<User>>`       | 
`std::vector<std::shared_ptr<User>>`      | `List<User>` + `@ListField(element: 
DeclaredType(ref: true))` | `List[User @Ref]`      |
+| `list<ref User>`        | `List<@Ref User>`                  | `List[User]`  
        | `[]*User` + `ref=false` | `Vec<Arc<User>>`      | 
`std::vector<std::shared_ptr<User>>`      | `List<User>` + `@ListField(element: 
DeclaredType(ref: true))` | `List[User @Ref]`      |
 
-Use `ref(thread_safe=true)` in Fory IDL (or `[(fory).thread_safe_pointer = 
true]` in protobuf)
-to generate `Arc` instead of `Rc` in Rust.
+Use `ref(thread_safe=false)` in Fory IDL (or
+`[(fory).thread_safe_pointer = false]` in protobuf) to generate `Rc` instead of
+`Arc` in Rust.
 
 ## Field Numbers
 
@@ -1339,15 +1340,15 @@ Underscore spellings for integer encoding are not FDL 
type names.
 
 #### Any
 
-| Language              | Type           | Notes                |
-| --------------------- | -------------- | -------------------- |
-| Java                  | `Object`       | Runtime type written |
-| Python                | `Any`          | Runtime type written |
-| Go                    | `any`          | Runtime type written |
-| Rust                  | `Box<dyn Any>` | Runtime type written |
-| C++                   | `std::any`     | Runtime type written |
-| JavaScript/TypeScript | `any`          | Runtime type written |
-| Dart                  | `Object?`      | Runtime type written |
+| Language              | Type                         | Notes                |
+| --------------------- | ---------------------------- | -------------------- |
+| Java                  | `Object`                     | Runtime type written |
+| Python                | `Any`                        | Runtime type written |
+| Go                    | `any`                        | Runtime type written |
+| Rust                  | `Arc<dyn Any + Send + Sync>` | Runtime type written |
+| C++                   | `std::any`                   | Runtime type written |
+| JavaScript/TypeScript | `any`                        | Runtime type written |
+| Dart                  | `Object?`                    | Runtime type written |
 
 **Example:**
 
@@ -1369,15 +1370,15 @@ message Envelope [id=122] {
 
 **Generated Code (`Envelope.payload`):**
 
-| Language              | Generated Field Type    |
-| --------------------- | ----------------------- |
-| Java                  | `Object payload`        |
-| Python                | `payload: Any`          |
-| Go                    | `Payload any`           |
-| Rust                  | `payload: Box<dyn Any>` |
-| C++                   | `std::any payload`      |
-| JavaScript/TypeScript | `payload: any`          |
-| Dart                  | `Object? payload`       |
+| Language              | Generated Field Type                  |
+| --------------------- | ------------------------------------- |
+| Java                  | `Object payload`                      |
+| Python                | `payload: Any`                        |
+| Go                    | `Payload any`                         |
+| Rust                  | `payload: Arc<dyn Any + Send + Sync>` |
+| C++                   | `std::any payload`                    |
+| JavaScript/TypeScript | `payload: any`                        |
+| Dart                  | `Object? payload`                     |
 
 **Notes:**
 
diff --git a/docs/guide/rust/configuration.md b/docs/guide/rust/configuration.md
index 83fc6e538..d140f0357 100644
--- a/docs/guide/rust/configuration.md
+++ b/docs/guide/rust/configuration.md
@@ -80,7 +80,7 @@ let fory = 
Fory::builder().xlang(true).max_dyn_depth(10).build(); // Allow up to
 
 **Protected types:**
 
-- `Box<dyn Any>`, `Rc<dyn Any>`, `Arc<dyn Any>`
+- `Box<dyn Any>`, `Rc<dyn Any>`, `Arc<dyn Any + Send + Sync>`
 - `Box<dyn Trait>`, `Rc<dyn Trait>`, `Arc<dyn Trait>` (trait objects)
 - `RcWeak<T>`, `ArcWeak<T>`
 - Collection types (Vec, HashMap, HashSet)
diff --git a/docs/guide/rust/native-serialization.md 
b/docs/guide/rust/native-serialization.md
index cba76f701..e2bf6e74f 100644
--- a/docs/guide/rust/native-serialization.md
+++ b/docs/guide/rust/native-serialization.md
@@ -100,7 +100,7 @@ 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>`.
+- Runtime type dispatch with `Rc<dyn Any>` and `Arc<dyn Any + Send + Sync>`.
 - 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 1f21d9eac..fa87735ed 100644
--- a/docs/guide/rust/polymorphism.md
+++ b/docs/guide/rust/polymorphism.md
@@ -85,7 +85,8 @@ assert_eq!(decoded.star_animal.speak(), "Woof!");
 
 ## Serializing dyn Any Trait Objects
 
-Apache Fory™ supports serializing `Rc<dyn Any>` and `Arc<dyn Any>` for runtime 
type dispatch:
+Apache Fory™ supports serializing `Rc<dyn Any>` and
+`Arc<dyn Any + Send + Sync>` for runtime type dispatch:
 
 **Key points:**
 
@@ -98,14 +99,11 @@ Apache Fory™ supports serializing `Rc<dyn Any>` and 
`Arc<dyn Any>` for runtime
 use std::rc::Rc;
 use std::any::Any;
 
-let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
+let dog_any: Rc<dyn Any> = Rc::new(Dog {
     name: "Rex".to_string(),
     breed: "Golden".to_string()
 });
 
-// Convert to Rc<dyn Any> for serialization
-let dog_any: Rc<dyn Any> = dog_rc.clone();
-
 // Serialize the Any wrapper
 let bytes = fory.serialize(&dog_any)?;
 let decoded: Rc<dyn Any> = fory.deserialize(&bytes)?;
@@ -115,22 +113,19 @@ let unwrapped = decoded.downcast_ref::<Dog>().unwrap();
 assert_eq!(unwrapped.name, "Rex");
 ```
 
-For thread-safe scenarios, use `Arc<dyn Any>`:
+For thread-safe scenarios, use `Arc<dyn Any + Send + Sync>`:
 
 ```rust
 use std::sync::Arc;
 use std::any::Any;
 
-let dog_arc: Arc<dyn Animal> = Arc::new(Dog {
+let dog_any: Arc<dyn Any + Send + Sync> = Arc::new(Dog {
     name: "Buddy".to_string(),
     breed: "Labrador".to_string()
 });
 
-// Convert to Arc<dyn Any>
-let dog_any: Arc<dyn Any> = dog_arc.clone();
-
 let bytes = fory.serialize(&dog_any)?;
-let decoded: Arc<dyn Any> = fory.deserialize(&bytes)?;
+let decoded: Arc<dyn Any + Send + Sync> = fory.deserialize(&bytes)?;
 
 // Downcast to concrete type
 let unwrapped = decoded.downcast_ref::<Dog>().unwrap();
@@ -185,7 +180,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>` instead (see the dyn Any section above).
+**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).
 
 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 588687075..2766641ca 100644
--- a/docs/guide/rust/schema-evolution.md
+++ b/docs/guide/rust/schema-evolution.md
@@ -134,7 +134,9 @@ For typed ADT unions whose schema cases are unit or 
single-payload variants,
 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.
+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.
 
 ### Enum Schema Evolution
 
diff --git a/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs 
b/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs
index 5d28da0f9..3e68444e1 100644
--- a/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs
+++ b/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs
@@ -16,11 +16,11 @@
 // under the License.
 
 use std::collections::HashMap;
-use std::rc::Rc;
+use std::sync::Arc;
 use std::{env, fs};
 
 use chrono::NaiveDate;
-use fory::{BFloat16, Float16, Fory, RcWeak};
+use fory::{ArcWeak, BFloat16, Float16, Fory};
 use idl_tests::generated::addressbook::{
     self,
     person::{PhoneNumber, PhoneType},
@@ -303,41 +303,41 @@ fn build_optional_holder() -> OptionalHolder {
 
 fn build_any_holder() -> AnyHolder {
     AnyHolder {
-        bool_value: Box::new(true),
-        string_value: Box::new("hello".to_string()),
-        date_value: Box::new(NaiveDate::from_ymd_opt(2024, 1, 2).unwrap()),
-        timestamp_value: Box::new(
+        bool_value: Arc::new(true),
+        string_value: Arc::new("hello".to_string()),
+        date_value: Arc::new(NaiveDate::from_ymd_opt(2024, 1, 2).unwrap()),
+        timestamp_value: Arc::new(
             NaiveDate::from_ymd_opt(2024, 1, 2)
                 .unwrap()
                 .and_hms_opt(3, 4, 5)
                 .expect("timestamp"),
         ),
-        message_value: Box::new(AnyInner {
+        message_value: Arc::new(AnyInner {
             name: "inner".to_string(),
         }),
-        union_value: Box::new(AnyUnion::Text("union".to_string())),
-        list_value: Box::new("list-placeholder".to_string()),
-        map_value: Box::new("map-placeholder".to_string()),
+        union_value: Arc::new(AnyUnion::Text("union".to_string())),
+        list_value: Arc::new("list-placeholder".to_string()),
+        map_value: Arc::new("map-placeholder".to_string()),
     }
 }
 
 fn build_any_holder_with_collections() -> AnyHolder {
     AnyHolder {
-        bool_value: Box::new(true),
-        string_value: Box::new("hello".to_string()),
-        date_value: Box::new(NaiveDate::from_ymd_opt(2024, 1, 2).unwrap()),
-        timestamp_value: Box::new(
+        bool_value: Arc::new(true),
+        string_value: Arc::new("hello".to_string()),
+        date_value: Arc::new(NaiveDate::from_ymd_opt(2024, 1, 2).unwrap()),
+        timestamp_value: Arc::new(
             NaiveDate::from_ymd_opt(2024, 1, 2)
                 .unwrap()
                 .and_hms_opt(3, 4, 5)
                 .expect("timestamp"),
         ),
-        message_value: Box::new(AnyInner {
+        message_value: Arc::new(AnyInner {
             name: "inner".to_string(),
         }),
-        union_value: Box::new(AnyUnion::Text("union".to_string())),
-        list_value: Box::new(vec!["alpha".to_string(), "beta".to_string()]),
-        map_value: Box::new(HashMap::from([
+        union_value: Arc::new(AnyUnion::Text("union".to_string())),
+        list_value: Arc::new(vec!["alpha".to_string(), "beta".to_string()]),
+        map_value: Arc::new(HashMap::from([
             ("k1".to_string(), "v1".to_string()),
             ("k2".to_string(), "v2".to_string()),
         ])),
@@ -638,31 +638,31 @@ fn assert_any_holder(holder: &AnyHolder) {
 }
 
 fn build_tree() -> tree::TreeNode {
-    let mut child_a = Rc::new(tree::TreeNode {
+    let mut child_a = Arc::new(tree::TreeNode {
         id: "child-a".to_string(),
         name: "child-a".to_string(),
         children: vec![],
         parent: None,
     });
-    let mut child_b = Rc::new(tree::TreeNode {
+    let mut child_b = Arc::new(tree::TreeNode {
         id: "child-b".to_string(),
         name: "child-b".to_string(),
         children: vec![],
         parent: None,
     });
 
-    let child_a_weak = RcWeak::from(&child_a);
-    let child_b_weak = RcWeak::from(&child_b);
-    Rc::get_mut(&mut child_a).expect("child a unique").parent = 
Some(child_b_weak);
-    Rc::get_mut(&mut child_b).expect("child b unique").parent = 
Some(child_a_weak);
+    let child_a_weak = ArcWeak::from(&child_a);
+    let child_b_weak = ArcWeak::from(&child_b);
+    Arc::get_mut(&mut child_a).expect("child a unique").parent = 
Some(child_b_weak);
+    Arc::get_mut(&mut child_b).expect("child b unique").parent = 
Some(child_a_weak);
 
     tree::TreeNode {
         id: "root".to_string(),
         name: "root".to_string(),
         children: vec![
-            Rc::clone(&child_a),
-            Rc::clone(&child_a),
-            Rc::clone(&child_b),
+            Arc::clone(&child_a),
+            Arc::clone(&child_a),
+            Arc::clone(&child_b),
         ],
         parent: None,
     }
@@ -670,8 +670,8 @@ fn build_tree() -> tree::TreeNode {
 
 fn assert_tree(root: &tree::TreeNode) {
     assert_eq!(root.children.len(), 3);
-    assert!(Rc::ptr_eq(&root.children[0], &root.children[1]));
-    assert!(!Rc::ptr_eq(&root.children[0], &root.children[2]));
+    assert!(Arc::ptr_eq(&root.children[0], &root.children[1]));
+    assert!(!Arc::ptr_eq(&root.children[0], &root.children[2]));
     let parent_a = root.children[0]
         .parent
         .as_ref()
@@ -684,36 +684,36 @@ fn assert_tree(root: &tree::TreeNode) {
         .expect("child b parent")
         .upgrade()
         .expect("upgrade child b parent");
-    assert!(Rc::ptr_eq(&parent_a, &root.children[2]));
-    assert!(Rc::ptr_eq(&parent_b, &root.children[0]));
+    assert!(Arc::ptr_eq(&parent_a, &root.children[2]));
+    assert!(Arc::ptr_eq(&parent_b, &root.children[0]));
 }
 
 fn build_graph() -> graph::Graph {
-    let mut node_a = Rc::new(graph::Node {
+    let mut node_a = Arc::new(graph::Node {
         id: "node-a".to_string(),
         out_edges: vec![],
         in_edges: vec![],
     });
-    let mut node_b = Rc::new(graph::Node {
+    let mut node_b = Arc::new(graph::Node {
         id: "node-b".to_string(),
         out_edges: vec![],
         in_edges: vec![],
     });
 
-    let edge = Rc::new(graph::Edge {
+    let edge = Arc::new(graph::Edge {
         id: "edge-1".to_string(),
         weight: 1.5_f32,
-        from: Some(RcWeak::from(&node_a)),
-        to: Some(RcWeak::from(&node_b)),
+        from: Some(ArcWeak::from(&node_a)),
+        to: Some(ArcWeak::from(&node_b)),
     });
 
-    Rc::get_mut(&mut node_a).expect("node a unique").out_edges = 
vec![Rc::clone(&edge)];
-    Rc::get_mut(&mut node_a).expect("node a unique").in_edges = 
vec![Rc::clone(&edge)];
-    Rc::get_mut(&mut node_b).expect("node b unique").in_edges = 
vec![Rc::clone(&edge)];
+    Arc::get_mut(&mut node_a).expect("node a unique").out_edges = 
vec![Arc::clone(&edge)];
+    Arc::get_mut(&mut node_a).expect("node a unique").in_edges = 
vec![Arc::clone(&edge)];
+    Arc::get_mut(&mut node_b).expect("node b unique").in_edges = 
vec![Arc::clone(&edge)];
 
     graph::Graph {
-        nodes: vec![Rc::clone(&node_a), Rc::clone(&node_b)],
-        edges: vec![Rc::clone(&edge)],
+        nodes: vec![Arc::clone(&node_a), Arc::clone(&node_b)],
+        edges: vec![Arc::clone(&edge)],
     }
 }
 
@@ -723,8 +723,8 @@ fn assert_graph(value: &graph::Graph) {
     let node_a = &value.nodes[0];
     let node_b = &value.nodes[1];
     let edge = &value.edges[0];
-    assert!(Rc::ptr_eq(&node_a.out_edges[0], &node_a.in_edges[0]));
-    assert!(Rc::ptr_eq(&node_a.out_edges[0], edge));
+    assert!(Arc::ptr_eq(&node_a.out_edges[0], &node_a.in_edges[0]));
+    assert!(Arc::ptr_eq(&node_a.out_edges[0], edge));
     let from = edge
         .from
         .as_ref()
@@ -737,8 +737,8 @@ fn assert_graph(value: &graph::Graph) {
         .expect("edge to")
         .upgrade()
         .expect("upgrade to");
-    assert!(Rc::ptr_eq(&from, node_a));
-    assert!(Rc::ptr_eq(&to, node_b));
+    assert!(Arc::ptr_eq(&from, node_a));
+    assert!(Arc::ptr_eq(&to, node_b));
 }
 
 #[test]
diff --git a/rust/README.md b/rust/README.md
index 3b381f690..dd9ea0708 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -248,7 +248,7 @@ The examples in this section use native mode because Rust 
trait objects and `dyn
 - `Box<dyn Trait>` - Owned trait objects
 - `Rc<dyn Trait>` - Reference-counted trait objects
 - `Arc<dyn Trait>` - Thread-safe reference-counted trait objects
-- `Box<dyn Any>`/`Rc<dyn Any>`/`Arc<dyn Any>` - Any trait type objects
+- `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
 
 **Basic Trait Object Serialization Example:**
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index 7983cc0c8..bb9ae8e53 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -63,7 +63,10 @@ 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 ReadCompatibleFn = fn(&mut ReadContext, Rc<TypeInfo>) -> Result<Box<dyn 
Any>, Error>;
+type ReadCompatibleSendSyncFn =
+    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>;
 const EMPTY_STRING: String = String::new();
@@ -87,44 +90,30 @@ pub struct Harness {
     read_fn: ReadFn,
     write_data_fn: WriteDataFn,
     read_data_fn: ReadDataFn,
+    read_data_send_sync_fn: ReadDataSendSyncFn,
     read_compatible_fn: Option<ReadCompatibleFn>,
+    read_compatible_send_sync_fn: Option<ReadCompatibleSendSyncFn>,
+    threadsafe: bool,
     to_serializer: ToSerializerFn,
     build_type_infos: BuildTypeInfosFn,
 }
 
 impl Harness {
-    pub fn new(
-        write_fn: WriteFn,
-        read_fn: ReadFn,
-        write_data_fn: WriteDataFn,
-        read_data_fn: ReadDataFn,
-        read_compatible_fn: Option<ReadCompatibleFn>,
-        to_serializer: ToSerializerFn,
-        build_type_infos: BuildTypeInfosFn,
-    ) -> Harness {
+    pub fn stub() -> Harness {
         Harness {
-            write_fn,
-            read_fn,
-            write_data_fn,
-            read_data_fn,
-            read_compatible_fn,
-            to_serializer,
-            build_type_infos,
+            write_fn: stub_write_fn,
+            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_compatible_fn: None,
+            read_compatible_send_sync_fn: None,
+            threadsafe: false,
+            to_serializer: stub_to_serializer_fn,
+            build_type_infos: stub_build_type_infos,
         }
     }
 
-    pub fn stub() -> Harness {
-        Harness::new(
-            stub_write_fn,
-            stub_read_fn,
-            stub_write_data_fn,
-            stub_read_data_fn,
-            None,
-            stub_to_serializer_fn,
-            stub_build_type_infos,
-        )
-    }
-
     #[inline(always)]
     pub fn get_write_fn(&self) -> WriteFn {
         self.write_fn
@@ -172,6 +161,29 @@ impl Harness {
         }
         (self.read_data_fn)(context)
     }
+
+    /// Reads polymorphic data for `Arc<dyn Any + Send + Sync>` carriers.
+    /// 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(
+        &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 {
+                return read_compatible_fn(context, typeinfo.clone());
+            }
+        }
+        (self.read_data_send_sync_fn)(context)
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -314,15 +326,18 @@ impl TypeInfo {
             h.clone()
         } else {
             // Create a stub harness that returns errors when called
-            Harness::new(
-                stub_write_fn,
-                stub_read_fn,
-                stub_write_data_fn,
-                stub_read_data_fn,
-                None,
-                stub_to_serializer_fn,
-                stub_build_type_infos,
-            )
+            Harness {
+                write_fn: stub_write_fn,
+                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_compatible_fn: None,
+                read_compatible_send_sync_fn: None,
+                threadsafe: false,
+                to_serializer: stub_to_serializer_fn,
+                build_type_infos: stub_build_type_infos,
+            }
         };
 
         TypeInfo {
@@ -369,6 +384,12 @@ 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> {
+    Err(Error::type_error(
+        "Cannot deserialize unknown remote type as Arc<dyn Any + Send + Sync> 
- type not registered locally",
+    ))
+}
+
 fn stub_to_serializer_fn(_: Box<dyn Any>) -> Result<Box<dyn Serializer>, 
Error> {
     Err(Error::type_error(
         "Cannot convert unknown remote type to serializer",
@@ -945,6 +966,20 @@ impl TypeResolver {
             }
         }
 
+        fn read_data_send_sync<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>())
+            }
+        }
+
         fn to_serializer<T2: 'static + Serializer>(
             boxed_any: Box<dyn Any>,
         ) -> Result<Box<dyn Serializer>, Error> {
@@ -967,15 +1002,26 @@ impl TypeResolver {
             Ok(Box::new(T2::fory_read_compatible(context, type_info)?))
         }
 
-        let harness = Harness::new(
-            write::<T>,
-            read::<T>,
-            write_data::<T>,
-            read_data::<T>,
-            Some(read_compatible::<T>),
-            to_serializer::<T>,
-            build_type_infos::<T>,
-        );
+        fn read_compatible_send_sync<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)
+        }
+
+        let harness = Harness {
+            write_fn: write::<T>,
+            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()),
+            to_serializer: to_serializer::<T>,
+            build_type_infos: build_type_infos::<T>,
+        };
         let type_info = TypeInfo::new(
             actual_type_id,
             user_type_id,
@@ -1166,6 +1212,20 @@ impl TypeResolver {
             }
         }
 
+        fn read_data_send_sync<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>())
+            }
+        }
+
         fn to_serializer<T2: 'static + Serializer>(
             boxed_any: Box<dyn Any>,
         ) -> Result<Box<dyn Serializer>, Error> {
@@ -1191,15 +1251,19 @@ impl TypeResolver {
         }
 
         // EXT types don't support fory_read_compatible
-        let harness = Harness::new(
-            write::<T>,
-            read::<T>,
-            write_data::<T>,
-            read_data::<T>,
-            None,
-            to_serializer::<T>,
-            build_type_infos::<T>,
-        );
+        let harness = Harness {
+            write_fn: write::<T>,
+            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: 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()),
+            to_serializer: to_serializer::<T>,
+            build_type_infos: build_type_infos::<T>,
+        };
 
         let user_type_id = if register_by_name {
             NO_USER_TYPE_ID
diff --git a/rust/fory-core/src/serializer/any.rs 
b/rust/fory-core/src/serializer/any.rs
index 9ac76de6c..e4e9067be 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -46,7 +46,7 @@ pub(crate) fn check_generic_container_type(type_info: 
&TypeInfo) -> Result<(), E
     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/Rc/Arc/Weak<dyn Any>. The serialization protocol does not 
preserve the element type \
+            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.",
@@ -322,6 +322,13 @@ impl Serializer for Rc<dyn Any> {
         true
     }
 
+    fn fory_is_threadsafe_type() -> bool
+    where
+        Self: Sized,
+    {
+        true
+    }
+
     fn fory_is_polymorphic() -> bool {
         true
     }
@@ -402,13 +409,13 @@ pub fn read_rc_any(
     }
 }
 
-impl ForyDefault for Arc<dyn Any> {
+impl ForyDefault for Arc<dyn Any + Send + Sync> {
     fn fory_default() -> Self {
         Arc::new(())
     }
 }
 
-impl Serializer for Arc<dyn Any> {
+impl Serializer for Arc<dyn Any + Send + Sync> {
     fn fory_write(
         &self,
         context: &mut WriteContext,
@@ -421,18 +428,19 @@ impl Serializer for Arc<dyn Any> {
                 .ref_writer
                 .try_write_arc_ref(&mut context.writer, self)
         {
-            let concrete_type_id: std::any::TypeId = (**self).type_id();
+            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(&**self, context, has_generics)?;
+                serializer_fn(value, context, has_generics)?;
             } else {
                 let serializer_fn = context
                     .get_type_info(&concrete_type_id)?
                     .get_harness()
                     .get_write_data_fn();
-                serializer_fn(&**self, context, has_generics)?;
+                serializer_fn(value, context, has_generics)?;
             }
         }
         Ok(())
@@ -470,24 +478,23 @@ impl Serializer for Arc<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 
Arc<dyn Any + Send + Sync> trait object"
+        ))
     }
 
     fn fory_get_type_id(_type_resolver: &TypeResolver) -> Result<TypeId, 
Error> {
         Err(Error::type_error(
-            "Arc<dyn Any> has no static type ID - use fory_type_id_dyn",
+            "Arc<dyn Any + Send + Sync> has no static type ID - use 
fory_type_id_dyn",
         ))
     }
 
     fn fory_type_id_dyn(&self, type_resolver: &TypeResolver) -> Result<TypeId, 
Error> {
-        resolve_registered_type_id(type_resolver, (**self).type_id())
+        resolve_registered_type_id(type_resolver, self.as_ref().type_id())
     }
 
     fn fory_concrete_type_id(&self) -> std::any::TypeId {
-        (**self).type_id()
+        self.as_ref().type_id()
     }
 
     fn fory_is_polymorphic() -> bool {
@@ -503,17 +510,31 @@ impl Serializer for Arc<dyn Any> {
     }
 
     fn fory_write_type_info(_context: &mut WriteContext) -> Result<(), Error> {
-        // Arc<dyn Any> is polymorphic - type info is written per element
+        // Arc<dyn Any + Send + Sync> is polymorphic - type info is written 
per element
         Ok(())
     }
 
     fn fory_read_type_info(_context: &mut ReadContext) -> Result<(), Error> {
-        // Arc<dyn Any> is polymorphic - type info is read per element
+        // Arc<dyn Any + Send + Sync> is polymorphic - type info is read per 
element
         Ok(())
     }
 
+    fn fory_read_data_send_sync(
+        context: &mut ReadContext,
+    ) -> Result<Box<dyn Any + Send + Sync>, Error>
+    where
+        Self: Sized + ForyDefault,
+    {
+        Ok(crate::serializer::box_send_sync(read_arc_any(
+            context,
+            RefMode::None,
+            true,
+            None,
+        )?))
+    }
+
     fn as_any(&self) -> &dyn Any {
-        &**self
+        self.as_ref()
     }
 }
 
@@ -522,21 +543,26 @@ pub fn read_arc_any(
     ref_mode: RefMode,
     read_type_info: bool,
     type_info: Option<Rc<TypeInfo>>,
-) -> Result<Arc<dyn Any>, Error> {
+) -> Result<Arc<dyn Any + Send + Sync>, Error> {
     let ref_flag = if ref_mode != RefMode::None {
         context.ref_reader.read_ref_flag(&mut context.reader)?
     } else {
         RefFlag::NotNullValue
     };
     match ref_flag {
-        RefFlag::Null => Err(Error::invalid_ref("Arc<dyn Any> cannot be 
null")),
+        RefFlag::Null => Err(Error::invalid_ref(
+            "Arc<dyn Any + Send + Sync> cannot be null",
+        )),
         RefFlag::Ref => {
             let ref_id = context.ref_reader.read_ref_id(&mut context.reader)?;
             context
                 .ref_reader
-                .get_arc_ref::<dyn Any>(ref_id)
+                .get_arc_ref::<dyn Any + Send + Sync>(ref_id)
                 .ok_or_else(|| {
-                    Error::invalid_data(format!("Arc<dyn Any> reference {} not 
found", ref_id))
+                    Error::invalid_data(format!(
+                        "Arc<dyn Any + Send + Sync> reference {} not found",
+                        ref_id
+                    ))
                 })
         }
         RefFlag::NotNullValue => {
@@ -544,32 +570,34 @@ pub fn read_arc_any(
             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 Arc<dyn Any>"))?
+                type_info.ok_or_else(|| {
+                    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)?;
             let boxed = typeinfo
                 .get_harness()
-                .read_polymorphic_data(context, &typeinfo)?;
+                .read_polymorphic_data_send_sync(context, &typeinfo)?;
             context.dec_depth();
-            Ok(Arc::<dyn Any>::from(boxed))
+            Ok(Arc::<dyn Any + Send + Sync>::from(boxed))
         }
         RefFlag::RefValue => {
             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 Arc<dyn Any>"))?
+                type_info.ok_or_else(|| {
+                    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)?;
             let boxed = typeinfo
                 .get_harness()
-                .read_polymorphic_data(context, &typeinfo)?;
+                .read_polymorphic_data_send_sync(context, &typeinfo)?;
             context.dec_depth();
-            let arc: Arc<dyn Any> = Arc::from(boxed);
+            let arc: Arc<dyn Any + Send + Sync> = Arc::from(boxed);
             context.ref_reader.store_arc_ref(arc.clone());
             Ok(arc)
         }
diff --git a/rust/fory-core/src/serializer/arc.rs 
b/rust/fory-core/src/serializer/arc.rs
index a67746820..5535dc706 100644
--- a/rust/fory-core/src/serializer/arc.rs
+++ b/rust/fory-core/src/serializer/arc.rs
@@ -29,6 +29,14 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static> 
Serializer for Arc<T>
         true
     }
 
+    #[inline(always)]
+    fn fory_is_threadsafe_type() -> bool
+    where
+        Self: Sized,
+    {
+        true
+    }
+
     fn fory_write(
         &self,
         context: &mut WriteContext,
@@ -120,6 +128,18 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static> 
Serializer for Arc<T>
         Ok(Arc::new(inner))
     }
 
+    #[inline]
+    fn fory_read_data_send_sync(
+        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,
+        )?))
+    }
+
     fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
         T::fory_read_type_info(context)
     }
diff --git a/rust/fory-core/src/serializer/codec.rs 
b/rust/fory-core/src/serializer/codec.rs
index 6ea272f06..d8006f692 100644
--- a/rust/fory-core/src/serializer/codec.rs
+++ b/rust/fory-core/src/serializer/codec.rs
@@ -2748,7 +2748,7 @@ macro_rules! any_codec {
 
 any_codec!(AnyBoxCodec, Box<dyn Any>);
 any_codec!(AnyRcCodec, Rc<dyn Any>);
-any_codec!(AnyArcCodec, Arc<dyn Any>);
+any_codec!(AnyArcCodec, Arc<dyn Any + Send + Sync>);
 
 #[cfg(test)]
 mod tests {
diff --git a/rust/fory-core/src/serializer/core.rs 
b/rust/fory-core/src/serializer/core.rs
index da4d91010..25a9f0c94 100644
--- a/rust/fory-core/src/serializer/core.rs
+++ b/rust/fory-core/src/serializer/core.rs
@@ -890,6 +890,35 @@ 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.
+    ///
+    /// 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(
+        context: &mut ReadContext,
+    ) -> Result<Box<dyn Any + Send + Sync>, Error>
+    where
+        Self: Sized + ForyDefault,
+    {
+        let _ = context;
+        Err(unsupported_threadsafe_type::<Self>())
+    }
+
     /// Read and validate type metadata from the buffer.
     ///
     /// This method reads type information to verify that the data in the 
buffer
@@ -1437,6 +1466,23 @@ pub trait StructSerializer: Serializer + 'static {
     ) -> Result<Self, Error>
     where
         Self: Sized;
+
+    /// Deserialize compatible data for dynamic thread-safe carriers.
+    ///
+    /// Implementations are generated only when the resulting struct/enum is
+    /// known to be `Send + Sync`.
+    #[inline(always)]
+    fn fory_read_compatible_send_sync(
+        context: &mut ReadContext,
+        type_info: Rc<TypeInfo>,
+    ) -> Result<Box<dyn Any + Send + Sync>, Error>
+    where
+        Self: Sized,
+    {
+        let _ = context;
+        let _ = type_info;
+        Err(unsupported_threadsafe_type::<Self>())
+    }
 }
 
 /// Serializes an object implementing `Serializer` to the write context.
@@ -1456,3 +1502,175 @@ 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/mod.rs 
b/rust/fory-core/src/serializer/mod.rs
index 8e8e23a41..5de0bc095 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -50,4 +50,8 @@ pub mod weak;
 mod core;
 mod decimal;
 pub use any::{read_box_any, write_box_any};
-pub use core::{read_data, write_data, ForyDefault, Serializer, 
StructSerializer};
+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};
diff --git a/rust/fory-core/src/serializer/trait_object.rs 
b/rust/fory-core/src/serializer/trait_object.rs
index 4eb634d4f..ce50ba1ac 100644
--- a/rust/fory-core/src/serializer/trait_object.rs
+++ b/rust/fory-core/src/serializer/trait_object.rs
@@ -49,7 +49,8 @@ macro_rules! downcast_and_serialize {
 /// This macro automatically generates serializers for `Box<dyn Trait>` trait 
objects.
 /// Due to Rust's orphan rules, only `Box<dyn Trait>` is supported for 
user-defined traits.
 /// For `Rc<dyn Trait>` and `Arc<dyn Trait>`, wrapper types are generated 
(e.g., `TraitRc`, `TraitArc`),
-/// either you use the wrapper types or use the `Rc<dyn Any>` or `Arc<dyn 
Any>` instead if it's not
+/// either you use the wrapper types or use `Rc<dyn Any>` or
+/// `Arc<dyn Any + Send + Sync>` instead if it's not
 /// inside struct fields. For struct fields, you can use the `Rc<dyn Trait>`, 
`Arc<dyn Trait>` directly,
 /// fory will generate converters for `Rc<dyn Trait>` and `Arc<dyn Trait>` to 
convert to wrapper for
 ///
diff --git a/rust/fory-core/src/serializer/unknown_case.rs 
b/rust/fory-core/src/serializer/unknown_case.rs
index 424be4ba2..7423f384e 100644
--- a/rust/fory-core/src/serializer/unknown_case.rs
+++ b/rust/fory-core/src/serializer/unknown_case.rs
@@ -48,7 +48,7 @@ fn write_typed_payload(context: &mut WriteContext, unknown: 
&UnknownCase) -> Res
     // polymorphic value. For internal numeric ids, the id byte is the complete
     // Any type metadata. Scalar Any payloads are not ref-tracked, so their ref
     // metadata is always NotNullValue before the original numeric encoding.
-    // Other types fall back to the normal Arc<dyn Any> path.
+    // Other types fall back to the normal Arc<dyn Any + Send + Sync> path.
     context.writer.write_i8(RefFlag::NotNullValue as i8);
     context.writer.write_u8(type_id as u8);
     match type_id {
@@ -130,7 +130,7 @@ pub fn read_payload(context: &mut ReadContext, case_id: 
u32) -> Result<UnknownCa
             let ref_id = context.ref_reader.read_ref_id(&mut context.reader)?;
             let value = context
                 .ref_reader
-                .get_arc_ref::<dyn Any>(ref_id)
+                .get_arc_ref::<dyn std::any::Any + Send + Sync>(ref_id)
                 .ok_or_else(|| {
                     Error::invalid_data(format!("UnknownCase ref {} not 
found", ref_id))
                 })?;
@@ -141,6 +141,14 @@ pub fn read_payload(context: &mut ReadContext, case_id: 
u32) -> Result<UnknownCa
             ))
         }
         RefFlag::NotNullValue | RefFlag::RefValue => {
+            let ref_id = if matches!(ref_flag, RefFlag::RefValue) {
+                // The wire ref id belongs to the unknown payload itself. 
Reserve it
+                // before reading nested payload fields so their own refs keep 
the
+                // same ids written by the normal reference engine.
+                Some(context.ref_reader.reserve_ref_id())
+            } else {
+                None
+            };
             // The unknown-case serializer owns only the union payload 
envelope. It must
             // not add a depth frame here: the decoded Any value is not a new 
nesting
             // boundary by itself, and real nested payload serializers perform 
their
@@ -149,10 +157,10 @@ pub fn read_payload(context: &mut ReadContext, case_id: 
u32) -> Result<UnknownCa
             check_generic_container_type(&type_info)?;
             let boxed = type_info
                 .get_harness()
-                .read_polymorphic_data(context, &type_info)?;
-            let value: Arc<dyn Any> = Arc::from(boxed);
-            if matches!(ref_flag, RefFlag::RefValue) {
-                context.ref_reader.store_arc_ref(value.clone());
+                .read_polymorphic_data_send_sync(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());
             }
             Ok(UnknownCase::from_runtime(
                 case_id,
@@ -200,6 +208,16 @@ impl Serializer for UnknownCase {
         read_payload(context, 0)
     }
 
+    fn fory_is_threadsafe_type() -> bool {
+        true
+    }
+
+    fn fory_read_data_send_sync(
+        context: &mut ReadContext,
+    ) -> Result<Box<dyn Any + Send + Sync>, Error> {
+        Ok(crate::serializer::box_send_sync(read_payload(context, 0)?))
+    }
+
     fn fory_get_type_id(_: &crate::resolver::TypeResolver) -> Result<TypeId, 
Error> {
         Ok(TypeId::UNKNOWN)
     }
diff --git a/rust/fory-core/src/types/unknown_case.rs 
b/rust/fory-core/src/types/unknown_case.rs
index b947c1a14..0496ef4f4 100644
--- a/rust/fory-core/src/types/unknown_case.rs
+++ b/rust/fory-core/src/types/unknown_case.rs
@@ -27,8 +27,8 @@ 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 the dynamic payload owned by Rust's existing polymorphic Arc path.
-    value: Arc<dyn Any>,
+    // plus a dynamic payload whose thread-safety is guaranteed by the trait 
object.
+    value: Arc<dyn Any + Send + Sync>,
 }
 
 impl UnknownCase {
@@ -38,7 +38,7 @@ impl UnknownCase {
     /// always uses the ordinary Any writer.
     pub fn new<T>(case_id: u32, value: T) -> Self
     where
-        T: Any,
+        T: Any + Send + Sync,
     {
         Self {
             case_id,
@@ -55,7 +55,7 @@ impl UnknownCase {
         self.type_id
     }
 
-    pub fn value(&self) -> &dyn Any {
+    pub fn value(&self) -> &(dyn Any + Send + Sync) {
         self.value.as_ref()
     }
 
@@ -63,11 +63,15 @@ impl UnknownCase {
         self.value.downcast_ref::<T>()
     }
 
-    pub(crate) fn value_arc(&self) -> &Arc<dyn Any> {
+    pub(crate) fn value_arc(&self) -> &Arc<dyn Any + Send + Sync> {
         &self.value
     }
 
-    pub(crate) fn from_runtime(case_id: u32, type_id: u32, value: Arc<dyn 
Any>) -> Self {
+    pub(crate) fn from_runtime(
+        case_id: u32,
+        type_id: u32,
+        value: Arc<dyn Any + Send + Sync>,
+    ) -> Self {
         Self {
             case_id,
             type_id,
@@ -117,6 +121,13 @@ mod tests {
         hasher.finish()
     }
 
+    #[test]
+    fn unknown_case_is_send_sync() {
+        fn assert_send_sync<T: Send + Sync>() {}
+
+        assert_send_sync::<UnknownCase>();
+    }
+
     #[test]
     fn equality_uses_carrier_identity() {
         let first = UnknownCase::new(7, String::from("future"));
@@ -130,7 +141,7 @@ mod tests {
 
     #[test]
     fn replay_metadata_does_not_affect_identity() {
-        let value: Arc<dyn Any> = Arc::new(String::from("future"));
+        let value: Arc<dyn Any + Send + Sync> = 
Arc::new(String::from("future"));
         let first = UnknownCase::from_runtime(7, 21, value.clone());
         let same_payload = UnknownCase::from_runtime(8, 5, value);
 
diff --git a/rust/fory-derive/src/object/field_codec.rs 
b/rust/fory-derive/src/object/field_codec.rs
index e00acad29..8543e044d 100644
--- a/rust/fory-derive/src/object/field_codec.rs
+++ b/rust/fory-derive/src/object/field_codec.rs
@@ -20,7 +20,9 @@ use super::field_meta::{
     ForyFieldMeta, IntEncoding,
 };
 use super::read::create_private_field_name;
-use super::util::get_type_id_by_type_ast;
+use super::util::{
+    get_type_id_by_type_ast, trait_object_is_any_send_sync, 
trait_object_is_any_without_auto_traits,
+};
 use crate::util::{is_arc_dyn_trait, is_box_dyn_trait, is_rc_dyn_trait, 
SourceField};
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote, ToTokens};
@@ -388,7 +390,7 @@ fn field_dispatch_for(
         && is_container_type(ty)
         && !is_vec_type(ty)
         && !contains_custom_trait_object(ty)
-        && !contains_exact_any_object(ty)
+        && !contains_any_object(ty)
     {
         return Ok(FieldDispatch::Serializer {
             field_type: field_type_expr_for(ty, nullable, track_ref)?,
@@ -531,8 +533,8 @@ pub(crate) fn codec_type_for(
                 )?;
                 if contains_custom_trait_object(key_ty)
                     || contains_custom_trait_object(value_ty)
-                    || contains_exact_any_object(key_ty)
-                    || contains_exact_any_object(value_ty)
+                    || contains_any_object(key_ty)
+                    || contains_any_object(value_ty)
                 {
                     return Ok(quote! {
                         ::fory_core::serializer::codec::HashMapCodec<#key_ty, 
#value_ty, #key_codec, #value_codec, #nullable, #track_ref>
@@ -737,14 +739,35 @@ pub(crate) fn codec_type_for(
         }
     }
 
-    if is_exact_any(ty, "Box") {
-        return Ok(quote! { 
::fory_core::serializer::codec::AnyBoxCodec<#nullable, #track_ref> });
+    if let Some(trait_obj) = any_trait_object_for(ty, "Box") {
+        if trait_object_is_any_without_auto_traits(trait_obj) {
+            return Ok(
+                quote! { 
::fory_core::serializer::codec::AnyBoxCodec<#nullable, #track_ref> },
+            );
+        }
+        return Ok(quote! {
+            compile_error!("Box<dyn Any> is the supported owned Any carrier")
+        });
     }
-    if is_exact_any(ty, "Rc") {
-        return Ok(quote! { 
::fory_core::serializer::codec::AnyRcCodec<#nullable, #track_ref> });
+    if let Some(trait_obj) = any_trait_object_for(ty, "Rc") {
+        if trait_object_is_any_without_auto_traits(trait_obj) {
+            return Ok(
+                quote! { ::fory_core::serializer::codec::AnyRcCodec<#nullable, 
#track_ref> },
+            );
+        }
+        return Ok(quote! {
+            compile_error!("Rc<dyn Any> is the supported single-thread Any 
carrier")
+        });
     }
-    if is_exact_any(ty, "Arc") {
-        return Ok(quote! { 
::fory_core::serializer::codec::AnyArcCodec<#nullable, #track_ref> });
+    if let Some(trait_obj) = any_trait_object_for(ty, "Arc") {
+        if trait_object_is_any_send_sync(trait_obj) {
+            return Ok(
+                quote! { 
::fory_core::serializer::codec::AnyArcCodec<#nullable, #track_ref> },
+            );
+        }
+        return Ok(quote! {
+            compile_error!("Arc<dyn Any> is not a shared thread-safe carrier; 
use Arc<dyn Any + Send + Sync>")
+        });
     }
 
     if let Some((_, trait_name)) = is_box_dyn_trait(ty) {
@@ -943,7 +966,7 @@ fn is_container_type(ty: &Type) -> bool {
 }
 
 fn contains_custom_trait_object(ty: &Type) -> bool {
-    if !is_exact_any(ty, "Box") && is_box_dyn_trait(ty).is_some() {
+    if any_trait_object_for(ty, "Box").is_none() && 
is_box_dyn_trait(ty).is_some() {
         return true;
     }
     if is_rc_dyn_trait(ty).is_some() || is_arc_dyn_trait(ty).is_some() {
@@ -967,22 +990,25 @@ fn contains_custom_trait_object(ty: &Type) -> bool {
     })
 }
 
-fn contains_exact_any_object(ty: &Type) -> bool {
-    if is_exact_any(ty, "Box") || is_exact_any(ty, "Rc") || is_exact_any(ty, 
"Arc") {
+fn contains_any_object(ty: &Type) -> bool {
+    if any_trait_object_for(ty, "Box").is_some()
+        || any_trait_object_for(ty, "Rc").is_some()
+        || any_trait_object_for(ty, "Arc").is_some()
+    {
         return true;
     }
     if let Some(inner) = extract_option_inner_type(ty) {
-        return contains_exact_any_object(&inner);
+        return contains_any_object(&inner);
     }
     if let Type::Array(array) = ty {
-        return contains_exact_any_object(array.elem.as_ref());
+        return contains_any_object(array.elem.as_ref());
     }
     let Some((_, Some(args))) = type_name_and_args(ty) else {
         return false;
     };
     args.iter().any(|arg| {
         if let GenericArgument::Type(ty) = arg {
-            contains_exact_any_object(ty)
+            contains_any_object(ty)
         } else {
             false
         }
@@ -1252,17 +1278,17 @@ fn validate_serializer_backed_map_meta(
     Ok(())
 }
 
-fn is_exact_any(ty: &Type, owner: &str) -> bool {
+fn any_trait_object_for<'a>(ty: &'a Type, owner: &str) -> Option<&'a 
syn::TypeTraitObject> {
     let Some((name, Some(args))) = type_name_and_args(ty) else {
-        return false;
+        return None;
     };
     if name != owner {
-        return false;
+        return None;
     }
     let Some(GenericArgument::Type(Type::TraitObject(trait_obj))) = 
args.first() else {
-        return false;
+        return None;
     };
-    trait_obj.bounds.iter().any(|bound| {
+    if trait_obj.bounds.iter().any(|bound| {
         if let syn::TypeParamBound::Trait(trait_bound) = bound {
             trait_bound
                 .path
@@ -1272,7 +1298,11 @@ fn is_exact_any(ty: &Type, owner: &str) -> bool {
         } else {
             false
         }
-    })
+    }) {
+        Some(trait_obj)
+    } else {
+        None
+    }
 }
 
 fn is_primitive_array_type(ty: &Type) -> bool {
diff --git a/rust/fory-derive/src/object/serializer.rs 
b/rust/fory-derive/src/object/serializer.rs
index db1212e40..5721dd9dc 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -52,6 +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();
 
     // StructSerializer
     let (
@@ -60,6 +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,
         enum_variant_meta_types,
     ) = match &ast.data {
         syn::Data::Struct(s) => {
@@ -76,6 +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(),
                 vec![], // No variant meta types for structs
             )
         }
@@ -92,6 +96,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs: 
ForyAttrs) -> TokenStrea
                     
::std::result::Result::Err(::fory_core::Error::not_allowed("`fory_read_compatible`
 should only be invoked at struct type"
                 ))
                 },
+                quote! {},
                 variant_meta_types,
             )
         }
@@ -179,6 +184,8 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs: 
ForyAttrs) -> TokenStrea
             fn fory_read_compatible(context: &mut ::fory_core::ReadContext, 
type_info: ::std::rc::Rc<::fory_core::TypeInfo>) -> ::std::result::Result<Self, 
::fory_core::error::Error> {
                 #read_compatible_ts
             }
+
+            #read_compatible_send_sync_ts
         }
 
         impl #impl_generics ::fory_core::Serializer for #name #ty_generics 
#where_clause {
@@ -243,6 +250,8 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs: 
ForyAttrs) -> TokenStrea
                 #read_data_ts
             }
 
+            #serializer_threadsafe_ts
+
             #[inline(always)]
             fn fory_read_type_info(context: &mut ::fory_core::ReadContext) -> 
::std::result::Result<(), ::fory_core::error::Error> {
                 #read_type_info_ts
@@ -254,6 +263,84 @@ pub fn derive_serializer(ast: &syn::DeriveInput, attrs: 
ForyAttrs) -> TokenStrea
     code
 }
 
+struct ThreadsafeTokens {
+    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 {
+            serializer: quote! {},
+            struct_read_compatible: quote! {},
+        };
+    }
+    let struct_read_compatible = if matches!(ast.data, syn::Data::Struct(_)) {
+        quote! {
+            #[inline]
+            fn fory_read_compatible_send_sync(
+                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> {
+                let value = <Self as 
::fory_core::StructSerializer>::fory_read_compatible(context, type_info)?;
+                
::std::result::Result::Ok(::fory_core::serializer::box_send_sync(value))
+            }
+        }
+    } else {
+        quote! {}
+    };
+    ThreadsafeTokens {
+        serializer: quote! {
+            #[inline(always)]
+            fn fory_is_threadsafe_type() -> bool
+            where
+                Self: Sized,
+            {
+                true
+            }
+
+            #[inline]
+            fn fory_read_data_send_sync(
+                context: &mut ::fory_core::ReadContext,
+            ) -> ::std::result::Result<::std::boxed::Box<dyn ::std::any::Any + 
Send + Sync>, ::fory_core::error::Error>
+            where
+                Self: Sized + ::fory_core::ForyDefault,
+            {
+                let value = <Self as 
::fory_core::Serializer>::fory_read_data(context)?;
+                
::std::result::Result::Ok(::fory_core::serializer::box_send_sync(value))
+            }
+        },
+        struct_read_compatible,
+    }
+}
+
+fn derive_type_is_threadsafe(ast: &syn::DeriveInput) -> bool {
+    use crate::object::util::{
+        all_type_params_send_sync, type_is_threadsafe, 
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.
+    if !all_type_params_send_sync(&ast.generics) {
+        return false;
+    }
+    let send_sync_params = type_param_send_sync_bounds(&ast.generics);
+    match &ast.data {
+        syn::Data::Struct(data) => data
+            .fields
+            .iter()
+            .all(|field| type_is_threadsafe(&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))
+        }),
+        syn::Data::Union(_) => false,
+    }
+}
+
 fn generate_default_impl(
     ast: &syn::DeriveInput,
     generate_default: bool,
diff --git a/rust/fory-derive/src/object/util.rs 
b/rust/fory-derive/src/object/util.rs
index f2c7e26d5..4fc38b893 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -21,6 +21,7 @@ use fory_core::util::to_snake_case;
 use proc_macro2::TokenStream;
 use quote::{quote, ToTokens};
 use std::cell::RefCell;
+use std::collections::HashSet;
 use syn::{Field, Fields, GenericArgument, Index, PathArguments, Type};
 
 /// Get field name for a field, handling both named and tuple struct fields.
@@ -122,7 +123,7 @@ fn is_forward_field_internal(ty: &Type, struct_name: &str) 
-> bool {
 
                 // Check smart pointers: Rc<T> / Arc<T>
                 // Only return true if:
-                // 1. Inner type is Rc<dyn Any> (polymorphic)
+                // 1. Inner type is dyn Any (polymorphic)
                 // 2. Inner type references the containing struct (forward 
reference)
                 if seg.ident == "Rc" || seg.ident == "Arc" {
                     if let PathArguments::AngleBracketed(args) = 
&seg.arguments {
@@ -133,12 +134,12 @@ fn is_forward_field_internal(ty: &Type, struct_name: 
&str) -> bool {
                                     if trait_obj
                                         .bounds
                                         .iter()
-                                        .any(|b| 
b.to_token_stream().to_string() == "Any")
+                                        .any(|b| 
trait_bound_ident(b).as_deref() == Some("Any"))
                                     {
-                                        // Rc<dyn Any> → return true
+                                        // Rc/Arc<dyn Any ...> needs 
polymorphic ref handling.
                                         return true;
                                     } else {
-                                        // Rc<dyn SomethingElse> → return false
+                                        // Rc/Arc<dyn SomethingElse> uses 
generated wrapper handling.
                                         return false;
                                     }
                                 }
@@ -929,6 +930,155 @@ pub(crate) fn is_unknown_case_type(ty: &Type) -> bool {
     )
 }
 
+pub(crate) fn type_param_send_sync_bounds(generics: &syn::Generics) -> 
HashSet<String> {
+    let mut params = HashSet::new();
+    for param in generics.type_params() {
+        if bounds_include_send_sync(&param.bounds) {
+            params.insert(param.ident.to_string());
+        }
+    }
+    if let Some(where_clause) = &generics.where_clause {
+        for predicate in &where_clause.predicates {
+            let syn::WherePredicate::Type(predicate_ty) = predicate else {
+                continue;
+            };
+            let Type::Path(type_path) = &predicate_ty.bounded_ty else {
+                continue;
+            };
+            let Some(segment) = type_path.path.segments.last() else {
+                continue;
+            };
+            if bounds_include_send_sync(&predicate_ty.bounds) {
+                params.insert(segment.ident.to_string());
+            }
+        }
+    }
+    params
+}
+
+pub(crate) fn all_type_params_send_sync(generics: &syn::Generics) -> bool {
+    let bounded = type_param_send_sync_bounds(generics);
+    generics
+        .type_params()
+        .all(|param| bounded.contains(&param.ident.to_string()))
+}
+
+pub(crate) fn type_is_threadsafe(ty: &Type, send_sync_params: 
&HashSet<String>) -> bool {
+    match ty {
+        Type::Array(array) => type_is_threadsafe(array.elem.as_ref(), 
send_sync_params),
+        Type::Tuple(tuple) => tuple
+            .elems
+            .iter()
+            .all(|elem| type_is_threadsafe(elem, send_sync_params)),
+        Type::Path(type_path) => {
+            let Some(segment) = type_path.path.segments.last() else {
+                return false;
+            };
+            let name = segment.ident.to_string();
+            if send_sync_params.contains(&name) && matches!(segment.arguments, 
PathArguments::None)
+            {
+                return true;
+            }
+            match name.as_str() {
+                "bool" | "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | 
"u8" | "u16" | "u32"
+                | "u64" | "u128" | "usize" | "f32" | "f64" | "String" | "Date" 
| "Timestamp"
+                | "Duration" | "Decimal" | "float16" | "bfloat16" | "Float16" 
| "BFloat16"
+                | "UnknownCase" => true,
+                "Rc" | "RcWeak" | "RefCell" | "Cell" => false,
+                "Option" | "Vec" | "VecDeque" | "LinkedList" | "HashSet" | 
"BTreeSet"
+                | "BinaryHeap" | "Box" | "Arc" | "ArcWeak" | "Mutex" => {
+                    let Some(inner) = first_type_arg(&segment.arguments) else {
+                        return false;
+                    };
+                    match (name.as_str(), inner) {
+                        ("Box", Type::TraitObject(_)) => false,
+                        ("Arc", Type::TraitObject(trait_obj)) => {
+                            trait_object_is_any_send_sync(trait_obj)
+                        }
+                        (_, Type::TraitObject(_)) => false,
+                        _ => type_is_threadsafe(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)
+                }
+                _ => true,
+            }
+        }
+        _ => false,
+    }
+}
+
+pub(crate) fn trait_object_is_any_send_sync(trait_obj: &syn::TypeTraitObject) 
-> bool {
+    trait_object_has_trait(trait_obj, "Any")
+        && trait_object_has_trait(trait_obj, "Send")
+        && trait_object_has_trait(trait_obj, "Sync")
+}
+
+pub(crate) fn trait_object_is_any_without_auto_traits(trait_obj: 
&syn::TypeTraitObject) -> bool {
+    trait_object_has_trait(trait_obj, "Any")
+        && !trait_object_has_trait(trait_obj, "Send")
+        && !trait_object_has_trait(trait_obj, "Sync")
+}
+
+fn first_type_arg(arguments: &PathArguments) -> Option<&Type> {
+    let PathArguments::AngleBracketed(args) = arguments else {
+        return None;
+    };
+    args.args.iter().find_map(|arg| match arg {
+        GenericArgument::Type(ty) => Some(ty),
+        _ => None,
+    })
+}
+
+fn two_path_type_args(arguments: &PathArguments) -> Option<(&Type, &Type)> {
+    let PathArguments::AngleBracketed(args) = arguments else {
+        return None;
+    };
+    let mut iter = args.args.iter().filter_map(|arg| match arg {
+        GenericArgument::Type(ty) => Some(ty),
+        _ => None,
+    });
+    Some((iter.next()?, iter.next()?))
+}
+
+fn bounds_include_send_sync(
+    bounds: &syn::punctuated::Punctuated<syn::TypeParamBound, 
syn::token::Plus>,
+) -> bool {
+    let mut has_send = false;
+    let mut has_sync = false;
+    for bound in bounds {
+        match trait_bound_ident(bound).as_deref() {
+            Some("Send") => has_send = true,
+            Some("Sync") => has_sync = true,
+            _ => {}
+        }
+    }
+    has_send && has_sync
+}
+
+fn trait_object_has_trait(trait_obj: &syn::TypeTraitObject, ident: &str) -> 
bool {
+    trait_obj
+        .bounds
+        .iter()
+        .any(|bound| trait_bound_ident(bound).as_deref() == Some(ident))
+}
+
+fn trait_bound_ident(bound: &syn::TypeParamBound) -> Option<String> {
+    let syn::TypeParamBound::Trait(trait_bound) = bound else {
+        return None;
+    };
+    trait_bound
+        .path
+        .segments
+        .last()
+        .map(|segment| segment.ident.to_string())
+}
+
 // The typed-ADT forward-compatibility carrier is selected by a runtime marker,
 // not by a schema case id. Known schema cases may still use id 0.
 pub(crate) fn is_runtime_unknown_variant(variant: &syn::Variant) -> bool {
diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs
index 78484d8ce..e190d0c8d 100644
--- a/rust/fory/src/lib.rs
+++ b/rust/fory/src/lib.rs
@@ -340,7 +340,7 @@
 //! - `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>` - Runtime type dispatch without custom 
traits
+//! - `Rc<dyn Any>` / `Arc<dyn Any + Send + Sync>` - Runtime type dispatch 
without custom traits
 //! - Collections: `Vec<Box<dyn Trait>>`, `HashMap<K, Box<dyn Trait>>`
 //!
 //! #### Basic Trait Object Serialization
@@ -401,8 +401,9 @@
 //!
 //! #### Serializing `dyn Any` Trait Objects
 //!
-//! **What it does:** Supports serializing `Rc<dyn Any>` and `Arc<dyn Any>` 
for maximum
-//! runtime type flexibility without defining custom traits.
+//! **What it does:** Supports serializing `Rc<dyn Any>` and
+//! `Arc<dyn Any + Send + Sync>` for maximum runtime type flexibility without
+//! defining custom traits.
 //!
 //! **When to use:** Plugin systems, dynamic type handling, or when you need 
runtime type
 //! dispatch without compile-time trait definitions.
@@ -440,7 +441,7 @@
 //! # }
 //! ```
 //!
-//! For thread-safe scenarios, use `Arc<dyn Any>`:
+//! For thread-safe scenarios, use `Arc<dyn Any + Send + Sync>`:
 //!
 //! ```rust
 //! use fory::Fory;
@@ -456,12 +457,12 @@
 //! let mut fory = Fory::builder().xlang(false).build();
 //! fory.register::<Cat>(101)?;
 //!
-//! let cat: Arc<dyn Any> = Arc::new(Cat {
+//! let cat: Arc<dyn Any + Send + Sync> = Arc::new(Cat {
 //!     name: "Whiskers".to_string()
 //! });
 //!
 //! let bytes = fory.serialize(&cat)?;
-//! let decoded: Arc<dyn Any> = fory.deserialize(&bytes)?;
+//! let decoded: Arc<dyn Any + Send + Sync> = fory.deserialize(&bytes)?;
 //!
 //! let unwrapped = decoded.downcast_ref::<Cat>().unwrap();
 //! assert_eq!(unwrapped.name, "Whiskers");
@@ -536,7 +537,7 @@
 //! 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>` instead (see the `dyn Any` section above).
+//! 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,7 +1006,7 @@
 //! - `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>` - Thread-safe runtime type dispatch
+//! - `Arc<dyn Any + Send + Sync>` - Thread-safe runtime type dispatch
 //!
 //! ## Wire Modes And Schema Evolution
 //!
diff --git a/rust/tests/tests/test_any.rs b/rust/tests/tests/test_any.rs
index df49db8af..d7c4a7b57 100644
--- a/rust/tests/tests/test_any.rs
+++ b/rust/tests/tests/test_any.rs
@@ -77,22 +77,22 @@ fn test_rc_dyn_any() {
 fn test_arc_dyn_any() {
     let fory = Fory::builder().xlang(false).build();
 
-    let value: Arc<dyn Any> = Arc::new("arc test".to_string());
+    let value: Arc<dyn Any + Send + Sync> = Arc::new("arc test".to_string());
     let bytes = fory.serialize(&value).unwrap();
-    let deserialized: Arc<dyn Any> = fory.deserialize(&bytes).unwrap();
+    let deserialized: Arc<dyn Any + Send + Sync> = 
fory.deserialize(&bytes).unwrap();
     assert_eq!(
         deserialized.downcast_ref::<String>().unwrap(),
         &"arc test".to_string()
     );
 
-    let value2: Arc<dyn Any> = Arc::new(123i32);
+    let value2: Arc<dyn Any + Send + Sync> = Arc::new(123i32);
     let bytes2 = fory.serialize(&value2).unwrap();
-    let deserialized2: Arc<dyn Any> = fory.deserialize(&bytes2).unwrap();
+    let deserialized2: Arc<dyn Any + Send + Sync> = 
fory.deserialize(&bytes2).unwrap();
     assert_eq!(deserialized2.downcast_ref::<i32>().unwrap(), &123i32);
 
-    let value3: Arc<dyn Any> = Arc::new(vec![1, 2, 3]);
+    let value3: Arc<dyn Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
     let bytes3 = fory.serialize(&value3).unwrap();
-    let deserialized3: Arc<dyn Any> = fory.deserialize(&bytes3).unwrap();
+    let deserialized3: Arc<dyn Any + Send + Sync> = 
fory.deserialize(&bytes3).unwrap();
     assert_eq!(
         deserialized3.downcast_ref::<Vec<i32>>().unwrap(),
         &vec![1, 2, 3]
@@ -122,12 +122,12 @@ 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> = Arc::new(vec![1, 2, 3]);
+    let shared_vec: Arc<dyn Any + Send + Sync> = Arc::new(vec![1, 2, 3]);
 
     let data = vec![shared_vec.clone(), shared_vec.clone()];
 
     let bytes = fory.serialize(&data).unwrap();
-    let deserialized: Vec<Arc<dyn Any>> = fory.deserialize(&bytes).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();
@@ -207,6 +207,11 @@ struct Container {
     items: Vec<String>,
 }
 
+#[derive(ForyStruct)]
+struct ArcAnyHolder {
+    value: Arc<dyn Any + Send + Sync>,
+}
+
 #[derive(ForyStruct)]
 struct AnyMapVarKey {
     #[fory(id = 0)]
@@ -257,17 +262,17 @@ fn test_arc_by_name() {
         items: vec!["a".to_string(), "b".to_string(), "c".to_string()],
     };
 
-    let value: Arc<dyn Any> = Arc::new(container);
+    let value: Arc<dyn Any + Send + Sync> = Arc::new(container);
     let bytes = fory.serialize(&value).unwrap();
-    let deserialized: Arc<dyn Any> = fory.deserialize(&bytes).unwrap();
+    let deserialized: Arc<dyn Any + Send + Sync> = 
fory.deserialize(&bytes).unwrap();
 
     let result = deserialized.downcast_ref::<Container>().unwrap();
     assert_eq!(result.id, 999);
     assert_eq!(result.items, vec!["a", "b", "c"]);
 
-    let container_vec: Vec<Arc<dyn Any>> = vec![value.clone(), value.clone()];
+    let container_vec: Vec<Arc<dyn Any + Send + Sync>> = vec![value.clone(), 
value.clone()];
     let bytes_vec = fory.serialize(&container_vec).unwrap();
-    let deserialized_vec: Vec<Arc<dyn Any>> = 
fory.deserialize(&bytes_vec).unwrap();
+    let deserialized_vec: Vec<Arc<dyn Any + Send + Sync>> = 
fory.deserialize(&bytes_vec).unwrap();
     assert_eq!(deserialized_vec.len(), 2);
     let first = deserialized_vec[0].downcast_ref::<Container>().unwrap();
     let second = deserialized_vec[1].downcast_ref::<Container>().unwrap();
@@ -278,6 +283,28 @@ fn test_arc_by_name() {
     ));
 }
 
+#[test]
+fn test_arc_any_field_by_name() {
+    let mut fory = Fory::builder().xlang(false).build();
+    fory.register_by_name::<Container>("", "Container").unwrap();
+    fory.register_by_name::<ArcAnyHolder>("", "ArcAnyHolder")
+        .unwrap();
+
+    let holder = ArcAnyHolder {
+        value: Arc::new(Container {
+            id: 777,
+            items: vec!["shared".to_string(), "any".to_string()],
+        }),
+    };
+
+    let bytes = fory.serialize(&holder).unwrap();
+    let decoded: ArcAnyHolder = fory.deserialize(&bytes).unwrap();
+    let container = decoded.value.downcast_ref::<Container>().unwrap();
+
+    assert_eq!(container.id, 777);
+    assert_eq!(container.items, vec!["shared", "any"]);
+}
+
 #[test]
 fn test_rc_by_name() {
     let mut fory = Fory::builder().xlang(false).build();
diff --git a/rust/tests/tests/test_enum.rs b/rust/tests/tests/test_enum.rs
index 0b33e3b75..566baff9d 100644
--- a/rust/tests/tests/test_enum.rs
+++ b/rust/tests/tests/test_enum.rs
@@ -222,6 +222,109 @@ fn union_compatible_enum_xlang_format() {
     assert_eq!(obj2, result2);
 }
 
+#[test]
+fn unknown_case_reads_threadsafe_generated_payload() {
+    use fory_core::ArcWeak;
+    use std::sync::{Arc, Mutex};
+
+    #[derive(ForyStruct, Debug)]
+    struct FutureLeaf {
+        label: String,
+    }
+
+    #[derive(ForyStruct, Debug)]
+    struct FutureNode {
+        value: i32,
+        parent: ArcWeak<Mutex<FutureNode>>,
+        children: Vec<Arc<Mutex<FutureNode>>>,
+    }
+
+    #[derive(ForyStruct, Debug)]
+    struct FuturePayload {
+        id: i32,
+        leaf: FutureLeaf,
+        primary: Arc<String>,
+        alias: Arc<String>,
+        root: Arc<Mutex<FutureNode>>,
+    }
+
+    #[derive(ForyUnion, Debug)]
+    enum OldUnion {
+        #[fory(unknown)]
+        Unknown(fory_core::UnknownCase),
+        #[fory(id = 0, default)]
+        Known(String),
+    }
+
+    #[derive(ForyUnion, Debug)]
+    enum NewUnion {
+        #[fory(unknown)]
+        Unknown(fory_core::UnknownCase),
+        #[fory(id = 0, default)]
+        Known(String),
+        #[fory(id = 1)]
+        Future(FuturePayload),
+    }
+
+    let mut writer = Fory::builder()
+        .xlang(true)
+        .compatible(false)
+        .track_ref(true)
+        .build();
+    writer.register::<FutureLeaf>(400).unwrap();
+    writer.register::<FuturePayload>(401).unwrap();
+    writer.register::<NewUnion>(402).unwrap();
+    writer.register::<FutureNode>(403).unwrap();
+
+    let mut reader = Fory::builder()
+        .xlang(true)
+        .compatible(false)
+        .track_ref(true)
+        .build();
+    reader.register::<FutureLeaf>(400).unwrap();
+    reader.register::<FuturePayload>(401).unwrap();
+    reader.register::<OldUnion>(402).unwrap();
+    reader.register::<FutureNode>(403).unwrap();
+
+    let shared = Arc::new("shared".to_string());
+    let root = Arc::new(Mutex::new(FutureNode {
+        value: 10,
+        parent: ArcWeak::new(),
+        children: vec![],
+    }));
+    let child = Arc::new(Mutex::new(FutureNode {
+        value: 20,
+        parent: ArcWeak::from(&root),
+        children: vec![],
+    }));
+    root.lock().unwrap().children.push(child);
+    let value = NewUnion::Future(FuturePayload {
+        id: 7,
+        leaf: FutureLeaf {
+            label: "nested".to_string(),
+        },
+        primary: shared.clone(),
+        alias: shared,
+        root,
+    });
+
+    let bytes = writer.serialize(&value).unwrap();
+    let decoded: OldUnion = reader.deserialize(&bytes).unwrap();
+
+    let OldUnion::Unknown(unknown) = decoded else {
+        panic!("expected unknown case");
+    };
+    assert_eq!(unknown.case_id(), 1);
+    let payload = unknown.downcast_ref::<FuturePayload>().unwrap();
+    assert_eq!(payload.id, 7);
+    assert_eq!(payload.leaf.label, "nested");
+    assert!(Arc::ptr_eq(&payload.primary, &payload.alias));
+    let root = payload.root.clone();
+    let child = root.lock().unwrap().children[0].clone();
+    let parent = child.lock().unwrap().parent.upgrade().unwrap();
+    assert!(Arc::ptr_eq(&root, &parent));
+}
+
 #[test]
 fn union_payload_nested_codec_annotations_roundtrip() {
     #[derive(ForyUnion, Debug, PartialEq)]
diff --git a/rust/tests/tests/test_helpers.rs b/rust/tests/tests/test_helpers.rs
index c3afe5dbf..3e9e62663 100644
--- a/rust/tests/tests/test_helpers.rs
+++ b/rust/tests/tests/test_helpers.rs
@@ -53,13 +53,13 @@ where
     assert_eq!(result.downcast_ref::<T>().unwrap(), &value);
 }
 
-/// Generic helper for testing Arc<dyn Any> serialization
+/// Generic helper for testing Arc<dyn Any + Send + Sync> serialization
 pub fn test_arc_any<T>(fory: &Fory, value: T)
 where
-    T: 'static + PartialEq + std::fmt::Debug + Clone,
+    T: 'static + PartialEq + std::fmt::Debug + Clone + Send + Sync,
 {
-    let wrapped: Arc<dyn Any> = Arc::new(value.clone());
+    let wrapped: Arc<dyn Any + Send + Sync> = Arc::new(value.clone());
     let bytes = fory.serialize(&wrapped).unwrap();
-    let result: Arc<dyn Any> = fory.deserialize(&bytes).unwrap();
+    let result: Arc<dyn Any + Send + Sync> = fory.deserialize(&bytes).unwrap();
     assert_eq!(result.downcast_ref::<T>().unwrap(), &value);
 }
diff --git a/rust/tests/tests/test_ref_resolver.rs 
b/rust/tests/tests/test_ref_resolver.rs
index 643c077c9..23c693947 100644
--- a/rust/tests/tests/test_ref_resolver.rs
+++ b/rust/tests/tests/test_ref_resolver.rs
@@ -20,6 +20,7 @@
 use fory_core::buffer::Writer;
 use fory_core::resolver::{RefReader, RefWriter};
 use fory_core::{ArcWeak, RcWeak};
+use std::any::Any;
 use std::rc::Rc;
 use std::sync::Arc;
 
@@ -79,6 +80,20 @@ fn test_arc_storage_and_retrieval() {
     assert!(Arc::ptr_eq(&arc, &retrieved));
 }
 
+#[test]
+fn test_arc_any_send_sync_storage_and_retrieval() {
+    let mut ref_reader = RefReader::new();
+    let arc: Arc<dyn Any + Send + Sync> = Arc::new(String::from("test"));
+
+    let ref_id = ref_reader.store_arc_ref(arc.clone());
+
+    let retrieved = ref_reader
+        .get_arc_ref::<dyn Any + Send + Sync>(ref_id)
+        .unwrap();
+    assert_eq!(retrieved.downcast_ref::<String>().unwrap(), "test");
+    assert!(Arc::ptr_eq(&arc, &retrieved));
+}
+
 #[test]
 fn test_ref_writer_clear() {
     let mut ref_writer = RefWriter::new();
diff --git a/rust/tests/tests/test_unsigned.rs 
b/rust/tests/tests/test_unsigned.rs
index a7085215b..4f4be267a 100644
--- a/rust/tests/tests/test_unsigned.rs
+++ b/rust/tests/tests/test_unsigned.rs
@@ -435,7 +435,7 @@ fn test_unsigned_with_smart_pointers() {
     test_rc_any(&fory, usize::MAX);
     test_rc_any(&fory, u128::MAX);
 
-    // Test Arc<dyn Any> with unsigned types
+    // Test Arc<dyn Any + Send + Sync> with unsigned types
     test_arc_any(&fory, u8::MAX);
     test_arc_any(&fory, u16::MAX);
     test_arc_any(&fory, u32::MAX);
@@ -459,7 +459,7 @@ fn test_unsigned_with_smart_pointers() {
     test_rc_any(&fory, vec![0usize, 1000000000000, usize::MAX]);
     test_rc_any(&fory, vec![0u128, 1000000000000, u128::MAX]);
 
-    // Test Arc<dyn Any> with unsigned arrays
+    // 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]);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to