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 1609d7c1f perf(rust): use thread local to manage fory rust 
WriteContext/ReadContext (#2946)
1609d7c1f is described below

commit 1609d7c1f2b2130dace15cb1de93238d4af445c0
Author: Shawn Yang <[email protected]>
AuthorDate: Sat Nov 29 16:38:51 2025 +0800

    perf(rust): use thread local to manage fory rust WriteContext/ReadContext 
(#2946)
    
    ## What does this PR do?
    
    - use thread local to manage fory rust WriteContext/ReadContext
    - remove pool
    
    ## Related issues
    
    Closes #2924
    
    #2945
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 rust/fory-core/src/fory.rs                   | 121 ++++++++++++++++++---------
 rust/fory-core/src/meta/type_meta.rs         |  21 +++++
 rust/fory-core/src/resolver/context.rs       |  81 ++++++++++++++++++
 rust/fory-core/src/resolver/mod.rs           |   1 -
 rust/fory-core/src/resolver/pool.rs          |  91 --------------------
 rust/fory-core/src/resolver/type_resolver.rs |  73 ++++++++++++++--
 rust/fory-core/src/serializer/any.rs         |   4 +-
 7 files changed, 253 insertions(+), 139 deletions(-)

diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index f3ecf8e70..83b609b67 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -18,8 +18,7 @@
 use crate::buffer::{Reader, Writer};
 use crate::ensure;
 use crate::error::Error;
-use crate::resolver::context::{ReadContext, WriteContext};
-use crate::resolver::pool::Pool;
+use crate::resolver::context::{ContextCache, ReadContext, WriteContext};
 use crate::resolver::type_resolver::TypeResolver;
 use crate::serializer::ForyDefault;
 use crate::serializer::{Serializer, StructSerializer};
@@ -28,9 +27,24 @@ use crate::types::{
     config_flags::{IS_CROSS_LANGUAGE_FLAG, IS_LITTLE_ENDIAN_FLAG},
     Language, MAGIC_NUMBER, SIZE_OF_REF_AND_TYPE,
 };
+use std::cell::UnsafeCell;
 use std::mem;
+use std::sync::atomic::{AtomicU64, Ordering};
 use std::sync::OnceLock;
 
+/// Global counter to assign unique IDs to each Fory instance.
+static FORY_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
+
+thread_local! {
+    /// Thread-local storage for WriteContext instances with fast path caching.
+    static WRITE_CONTEXTS: UnsafeCell<ContextCache<WriteContext<'static>>> =
+        UnsafeCell::new(ContextCache::new());
+
+    /// Thread-local storage for ReadContext instances with fast path caching.
+    static READ_CONTEXTS: UnsafeCell<ContextCache<ReadContext<'static>>> =
+        UnsafeCell::new(ContextCache::new());
+}
+
 /// The main Fory serialization framework instance.
 ///
 /// `Fory` provides high-performance cross-language serialization and 
deserialization
@@ -75,6 +89,8 @@ use std::sync::OnceLock;
 ///     .max_dyn_depth(10);
 /// ```
 pub struct Fory {
+    /// Unique identifier for this Fory instance, used as key in thread-local 
context maps.
+    id: u64,
     compatible: bool,
     xlang: bool,
     share_meta: bool,
@@ -82,14 +98,14 @@ pub struct Fory {
     compress_string: bool,
     max_dyn_depth: u32,
     check_struct_version: bool,
-    // Lazy-initialized pools (thread-safe, one-time initialization)
-    write_context_pool: OnceLock<Result<Pool<Box<WriteContext<'static>>>, 
Error>>,
-    read_context_pool: OnceLock<Result<Pool<Box<ReadContext<'static>>>, 
Error>>,
+    /// Lazy-initialized final type resolver (thread-safe, one-time 
initialization).
+    final_type_resolver: OnceLock<Result<TypeResolver, Error>>,
 }
 
 impl Default for Fory {
     fn default() -> Self {
         Fory {
+            id: FORY_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
             compatible: false,
             xlang: false,
             share_meta: false,
@@ -97,8 +113,7 @@ impl Default for Fory {
             compress_string: false,
             max_dyn_depth: 5,
             check_struct_version: false,
-            write_context_pool: OnceLock::new(),
-            read_context_pool: OnceLock::new(),
+            final_type_resolver: OnceLock::new(),
         }
     }
 }
@@ -363,8 +378,7 @@ impl Fory {
     /// let bytes = fory.serialize(&point);
     /// ```
     pub fn serialize<T: Serializer>(&self, record: &T) -> Result<Vec<u8>, 
Error> {
-        let pool = self.get_writer_pool()?;
-        pool.borrow_mut(
+        self.with_write_context(
             |context| match self.serialize_with_context(record, context) {
                 Ok(_) => {
                     let result = context.writer.dump();
@@ -499,10 +513,10 @@ impl Fory {
         record: &T,
         buf: &mut Vec<u8>,
     ) -> Result<usize, Error> {
-        let pool = self.get_writer_pool()?;
         let start = buf.len();
-        pool.borrow_mut(|context| {
-            // Context go from pool would be 'static. but context hold the 
buffer through `writer` field, so we should make buffer live longer.
+        self.with_write_context(|context| {
+            // Context from thread-local would be 'static. but context hold 
the buffer through `writer` field,
+            // so we should make buffer live longer.
             // After serializing, `detach_writer` will be called, the writer 
in context will be set to dangling pointer.
             // So it's safe to make buf live to the end of this method.
             let outlive_buffer = unsafe { mem::transmute::<&mut Vec<u8>, &mut 
Vec<u8>>(buf) };
@@ -517,31 +531,51 @@ impl Fory {
         })
     }
 
+    /// Gets the final type resolver, building it lazily on first access.
     #[inline(always)]
-    fn get_writer_pool(&self) -> Result<&Pool<Box<WriteContext<'static>>>, 
Error> {
-        let pool_result = self.write_context_pool.get_or_init(|| {
-            let type_resolver = 
self.type_resolver.build_final_type_resolver()?;
+    fn get_final_type_resolver(&self) -> Result<&TypeResolver, Error> {
+        let result = self
+            .final_type_resolver
+            .get_or_init(|| self.type_resolver.build_final_type_resolver());
+        result
+            .as_ref()
+            .map_err(|e| Error::type_error(format!("Failed to build type 
resolver: {}", e)))
+    }
+
+    /// Executes a closure with mutable access to a WriteContext for this Fory 
instance.
+    /// The context is stored in thread-local storage, eliminating all lock 
contention.
+    /// Uses fast path caching for O(1) access when using the same Fory 
instance repeatedly.
+    #[inline(always)]
+    fn with_write_context<R>(
+        &self,
+        f: impl FnOnce(&mut WriteContext) -> Result<R, Error>,
+    ) -> Result<R, Error> {
+        // SAFETY: Thread-local storage is only accessed from the current 
thread.
+        // We use UnsafeCell to avoid RefCell's runtime borrow checking 
overhead.
+        // The closure `f` does not recursively call with_write_context, so 
there's no aliasing.
+        WRITE_CONTEXTS.with(|cache| {
+            let cache = unsafe { &mut *cache.get() };
+            let id = self.id;
             let compatible = self.compatible;
             let share_meta = self.share_meta;
             let compress_string = self.compress_string;
             let xlang = self.xlang;
             let check_struct_version = self.check_struct_version;
 
-            let factory = move || {
-                Box::new(WriteContext::new(
+            let context = cache.get_or_insert_result(id, || {
+                // Only fetch type resolver when creating a new context
+                let type_resolver = self.get_final_type_resolver()?;
+                Ok(Box::new(WriteContext::new(
                     type_resolver.clone(),
                     compatible,
                     share_meta,
                     compress_string,
                     xlang,
                     check_struct_version,
-                ))
-            };
-            Ok(Pool::new(factory))
-        });
-        pool_result
-            .as_ref()
-            .map_err(|e| Error::type_error(format!("Failed to build type 
resolver: {}", e)))
+                )))
+            })?;
+            f(context)
+        })
     }
 
     /// Serializes a value of type `T` into a byte vector.
@@ -823,8 +857,7 @@ impl Fory {
     /// let deserialized: Point = fory.deserialize(&bytes).unwrap();
     /// ```
     pub fn deserialize<T: Serializer + ForyDefault>(&self, bf: &[u8]) -> 
Result<T, Error> {
-        let pool = self.get_read_pool()?;
-        pool.borrow_mut(|context| {
+        self.with_read_context(|context| {
             context.init(self.max_dyn_depth);
             let outlive_buffer = unsafe { mem::transmute::<&[u8], &[u8]>(bf) };
             context.attach_reader(Reader::new(outlive_buffer));
@@ -885,8 +918,7 @@ impl Fory {
         &self,
         reader: &mut Reader,
     ) -> Result<T, Error> {
-        let pool = self.get_read_pool()?;
-        pool.borrow_mut(|context| {
+        self.with_read_context(|context| {
             context.init(self.max_dyn_depth);
             let outlive_buffer = unsafe { mem::transmute::<&[u8], 
&[u8]>(reader.bf) };
             let mut new_reader = Reader::new(outlive_buffer);
@@ -899,31 +931,40 @@ impl Fory {
         })
     }
 
+    /// Executes a closure with mutable access to a ReadContext for this Fory 
instance.
+    /// The context is stored in thread-local storage, eliminating all lock 
contention.
+    /// Uses fast path caching for O(1) access when using the same Fory 
instance repeatedly.
     #[inline(always)]
-    fn get_read_pool(&self) -> Result<&Pool<Box<ReadContext<'static>>>, Error> 
{
-        let pool_result = self.read_context_pool.get_or_init(|| {
-            let type_resolver = 
self.type_resolver.build_final_type_resolver()?;
+    fn with_read_context<R>(
+        &self,
+        f: impl FnOnce(&mut ReadContext) -> Result<R, Error>,
+    ) -> Result<R, Error> {
+        // SAFETY: Thread-local storage is only accessed from the current 
thread.
+        // We use UnsafeCell to avoid RefCell's runtime borrow checking 
overhead.
+        // The closure `f` does not recursively call with_read_context, so 
there's no aliasing.
+        READ_CONTEXTS.with(|cache| {
+            let cache = unsafe { &mut *cache.get() };
+            let id = self.id;
             let compatible = self.compatible;
             let share_meta = self.share_meta;
             let xlang = self.xlang;
             let max_dyn_depth = self.max_dyn_depth;
             let check_struct_version = self.check_struct_version;
 
-            let factory = move || {
-                Box::new(ReadContext::new(
+            let context = cache.get_or_insert_result(id, || {
+                // Only fetch type resolver when creating a new context
+                let type_resolver = self.get_final_type_resolver()?;
+                Ok(Box::new(ReadContext::new(
                     type_resolver.clone(),
                     compatible,
                     share_meta,
                     xlang,
                     max_dyn_depth,
                     check_struct_version,
-                ))
-            };
-            Ok(Pool::new(factory))
-        });
-        pool_result
-            .as_ref()
-            .map_err(|e| Error::type_error(format!("Failed to build type 
resolver: {}", e)))
+                )))
+            })?;
+            f(context)
+        })
     }
 
     #[inline(always)]
diff --git a/rust/fory-core/src/meta/type_meta.rs 
b/rust/fory-core/src/meta/type_meta.rs
index 641fa7be7..91199c507 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -337,6 +337,18 @@ impl TypeMetaLayer {
         &self.field_infos
     }
 
+    /// Creates a deep clone with new Rc instances.
+    /// This is safe for concurrent use from multiple threads.
+    pub fn deep_clone(&self) -> TypeMetaLayer {
+        TypeMetaLayer {
+            type_id: self.type_id,
+            namespace: Rc::new((*self.namespace).clone()),
+            type_name: Rc::new((*self.type_name).clone()),
+            register_by_name: self.register_by_name,
+            field_infos: self.field_infos.clone(),
+        }
+    }
+
     fn write_name(writer: &mut Writer, name: &MetaString, encodings: 
&[Encoding]) {
         let encoding_idx = encodings.iter().position(|x| *x == 
name.encoding).unwrap() as u8;
         let bytes = name.bytes.as_slice();
@@ -636,6 +648,15 @@ impl TypeMeta {
         }
     }
 
+    /// Creates a deep clone with new Rc instances.
+    /// This is safe for concurrent use from multiple threads.
+    pub fn deep_clone(&self) -> TypeMeta {
+        TypeMeta {
+            hash: self.hash,
+            layer: self.layer.deep_clone(),
+        }
+    }
+
     pub(crate) fn from_fields(
         type_id: u32,
         namespace: MetaString,
diff --git a/rust/fory-core/src/resolver/context.rs 
b/rust/fory-core/src/resolver/context.rs
index 7961fc2fe..8c937278b 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 use crate::buffer::{Reader, Writer};
+use std::collections::HashMap;
 use std::mem;
 
 use crate::error::Error;
@@ -27,6 +28,86 @@ use crate::resolver::type_resolver::{TypeInfo, TypeResolver};
 use crate::types;
 use std::rc::Rc;
 
+/// Thread-local context cache with fast path for single Fory instance.
+/// Uses (cached_id, context) for O(1) access when using same Fory instance 
repeatedly.
+/// Falls back to HashMap for multiple Fory instances per thread.
+pub struct ContextCache<T> {
+    /// Fast path: cached context for the most recently used Fory instance
+    cached_id: u64,
+    cached_context: Option<Box<T>>,
+    /// Slow path: HashMap for other Fory instances
+    others: HashMap<u64, Box<T>>,
+}
+
+impl<T> ContextCache<T> {
+    pub fn new() -> Self {
+        ContextCache {
+            cached_id: u64::MAX,
+            cached_context: None,
+            others: HashMap::new(),
+        }
+    }
+
+    #[inline(always)]
+    pub fn get_or_insert(&mut self, id: u64, create: impl FnOnce() -> Box<T>) 
-> &mut T {
+        if self.cached_id == id {
+            // Fast path: same Fory instance as last time
+            return self.cached_context.as_mut().unwrap();
+        }
+
+        // Check if we need to swap with cached
+        if self.cached_context.is_some() {
+            // Move current cached to others
+            let old_id = self.cached_id;
+            let old_context = self.cached_context.take().unwrap();
+            self.others.insert(old_id, old_context);
+        }
+
+        // Get or create context for new id
+        let context = self.others.remove(&id).unwrap_or_else(create);
+        self.cached_id = id;
+        self.cached_context = Some(context);
+        self.cached_context.as_mut().unwrap()
+    }
+
+    /// Like `get_or_insert`, but the create closure returns a Result.
+    /// This allows error handling during context creation without 
pre-fetching resources.
+    #[inline(always)]
+    pub fn get_or_insert_result<E>(
+        &mut self,
+        id: u64,
+        create: impl FnOnce() -> Result<Box<T>, E>,
+    ) -> Result<&mut T, E> {
+        if self.cached_id == id {
+            // Fast path: same Fory instance as last time
+            return Ok(self.cached_context.as_mut().unwrap());
+        }
+
+        // Check if we need to swap with cached
+        if self.cached_context.is_some() {
+            // Move current cached to others
+            let old_id = self.cached_id;
+            let old_context = self.cached_context.take().unwrap();
+            self.others.insert(old_id, old_context);
+        }
+
+        // Get or create context for new id
+        let context = match self.others.remove(&id) {
+            Some(ctx) => ctx,
+            None => create()?,
+        };
+        self.cached_id = id;
+        self.cached_context = Some(context);
+        Ok(self.cached_context.as_mut().unwrap())
+    }
+}
+
+impl<T> Default for ContextCache<T> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 /// Serialization state container used on a single thread at a time.
 /// Sharing the same instance across threads simultaneously causes undefined 
behavior.
 #[allow(clippy::needless_lifetimes)]
diff --git a/rust/fory-core/src/resolver/mod.rs 
b/rust/fory-core/src/resolver/mod.rs
index ad58e725e..35a3538fa 100644
--- a/rust/fory-core/src/resolver/mod.rs
+++ b/rust/fory-core/src/resolver/mod.rs
@@ -18,6 +18,5 @@
 pub mod context;
 pub mod meta_resolver;
 pub mod meta_string_resolver;
-pub mod pool;
 pub mod ref_resolver;
 pub mod type_resolver;
diff --git a/rust/fory-core/src/resolver/pool.rs 
b/rust/fory-core/src/resolver/pool.rs
deleted file mode 100644
index b1284a288..000000000
--- a/rust/fory-core/src/resolver/pool.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-use crate::util::Spinlock;
-use std::cell::Cell;
-use std::sync::atomic::{AtomicU64, Ordering};
-
-/// Number of segments in the pool. Using 16 segments to reduce contention.
-const NUM_SEGMENTS: usize = 16;
-
-/// Global counter to assign unique IDs to threads for segment selection.
-static THREAD_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
-
-thread_local! {
-    /// Cached segment index for the current thread.
-    /// Using a simple incrementing counter ensures even distribution across 
segments.
-    static SEGMENT_INDEX: Cell<usize> = Cell::new(
-        (THREAD_ID_COUNTER.fetch_add(1, Ordering::Relaxed) as usize) % 
NUM_SEGMENTS
-    );
-}
-
-/// A segment containing a spinlock-protected vector of pooled items.
-struct Segment<T> {
-    items: Spinlock<Vec<T>>,
-}
-
-impl<T> Segment<T> {
-    fn new() -> Self {
-        Segment {
-            items: Spinlock::new(Vec::new()),
-        }
-    }
-
-    #[inline(always)]
-    fn get(&self, factory: &dyn Fn() -> T) -> T {
-        self.items.lock().pop().unwrap_or_else(factory)
-    }
-
-    #[inline(always)]
-    fn put(&self, item: T) {
-        self.items.lock().push(item);
-    }
-}
-
-/// A segmented object pool that reduces lock contention by distributing
-/// access across multiple segments based on thread ID.
-///
-/// Each thread is assigned to a specific segment, so threads accessing
-/// the pool concurrently will typically hit different locks, reducing 
contention.
-pub struct Pool<T> {
-    segments: [Segment<T>; NUM_SEGMENTS],
-    factory: Box<dyn Fn() -> T + Send + Sync>,
-}
-
-impl<T> Pool<T> {
-    pub fn new<F>(factory: F) -> Self
-    where
-        F: Fn() -> T + Send + Sync + 'static,
-    {
-        Pool {
-            segments: std::array::from_fn(|_| Segment::new()),
-            factory: Box::new(factory),
-        }
-    }
-
-    /// Borrows an item from the pool, executes the handler, and returns the 
item to the pool.
-    #[inline(always)]
-    pub fn borrow_mut<Result>(&self, handler: impl FnOnce(&mut T) -> Result) 
-> Result {
-        let segment_idx = SEGMENT_INDEX.with(|idx| idx.get());
-        let segment = &self.segments[segment_idx];
-
-        let mut obj = segment.get(&*self.factory);
-        let result = handler(&mut obj);
-        segment.put(obj);
-        result
-    }
-}
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index 2709cc837..d39dde005 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -199,6 +199,20 @@ impl TypeInfo {
         &self.harness
     }
 
+    /// Creates a deep clone with new Rc instances.
+    /// This is safe for concurrent use from multiple threads.
+    pub fn deep_clone(&self) -> TypeInfo {
+        TypeInfo {
+            type_def: Rc::new((*self.type_def).clone()),
+            type_meta: Rc::new(self.type_meta.deep_clone()),
+            type_id: self.type_id,
+            namespace: Rc::new((*self.namespace).clone()),
+            type_name: Rc::new((*self.type_name).clone()),
+            register_by_name: self.register_by_name,
+            harness: self.harness.clone(),
+        }
+    }
+
     /// Create a TypeInfo from remote TypeMeta with a stub harness
     /// Used when the type doesn't exist locally during deserialization
     pub fn from_remote_meta(
@@ -1026,17 +1040,66 @@ impl TypeResolver {
     ///
     /// # Returns
     ///
-    /// A shallow clone of the TypeResolver with all internal maps cloned.
+    /// A deep clone of the TypeResolver with all internal Rc instances 
recreated.
+    /// This ensures thread safety when cloning from multiple threads 
simultaneously.
     ///
     /// # See Also
     ///
     /// - [`build_final_type_resolver`](Self::build_final_type_resolver) - 
Builds a complete resolver
     pub(crate) fn clone(&self) -> TypeResolver {
+        // Build a mapping from old Rc<TypeInfo> pointers to new Rc<TypeInfo>
+        // to ensure we reuse the same new Rc for the same original TypeInfo
+        let mut type_info_mapping: HashMap<*const TypeInfo, Rc<TypeInfo>> = 
HashMap::new();
+
+        // Helper closure to get or create deep-cloned TypeInfo wrapped in new 
Rc
+        let mut get_or_clone_type_info = |rc: &Rc<TypeInfo>| -> Rc<TypeInfo> {
+            let ptr = Rc::as_ptr(rc);
+            if let Some(new_rc) = type_info_mapping.get(&ptr) {
+                new_rc.clone()
+            } else {
+                let new_rc = Rc::new(rc.deep_clone());
+                type_info_mapping.insert(ptr, new_rc.clone());
+                new_rc
+            }
+        };
+
+        // Clone all maps with deep-cloned TypeInfo in new Rc wrappers
+        let type_info_map_by_id: HashMap<u32, Rc<TypeInfo>> = self
+            .type_info_map_by_id
+            .iter()
+            .map(|(k, v)| (*k, get_or_clone_type_info(v)))
+            .collect();
+
+        let type_info_map: HashMap<std::any::TypeId, Rc<TypeInfo>> = self
+            .type_info_map
+            .iter()
+            .map(|(k, v)| (*k, get_or_clone_type_info(v)))
+            .collect();
+
+        let type_info_map_by_name: HashMap<(String, String), Rc<TypeInfo>> = 
self
+            .type_info_map_by_name
+            .iter()
+            .map(|(k, v)| (k.clone(), get_or_clone_type_info(v)))
+            .collect();
+
+        // Deep clone the MetaString keys as well
+        let type_info_map_by_meta_string_name: HashMap<
+            (Rc<MetaString>, Rc<MetaString>),
+            Rc<TypeInfo>,
+        > = self
+            .type_info_map_by_meta_string_name
+            .iter()
+            .map(|(k, v)| {
+                let new_key = (Rc::new((*k.0).clone()), 
Rc::new((*k.1).clone()));
+                (new_key, get_or_clone_type_info(v))
+            })
+            .collect();
+
         TypeResolver {
-            type_info_map_by_id: self.type_info_map_by_id.clone(),
-            type_info_map: self.type_info_map.clone(),
-            type_info_map_by_name: self.type_info_map_by_name.clone(),
-            type_info_map_by_meta_string_name: 
self.type_info_map_by_meta_string_name.clone(),
+            type_info_map_by_id,
+            type_info_map,
+            type_info_map_by_name,
+            type_info_map_by_meta_string_name,
             partial_type_infos: HashMap::new(),
             type_id_index: self.type_id_index.clone(),
             compatible: self.compatible,
diff --git a/rust/fory-core/src/serializer/any.rs 
b/rust/fory-core/src/serializer/any.rs
index f90737a87..29d85ad27 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -29,8 +29,8 @@ use std::rc::Rc;
 use std::sync::Arc;
 
 /// Check if the type info represents a generic container type (LIST, SET, 
MAP).
-/// These types cannot be deserialized polymorphically via Box<dyn Any> because
-/// different generic instantiations (e.g., Vec<A>, Vec<B>) share the same 
type ID.
+/// These types cannot be deserialized polymorphically via `Box<dyn Any>` 
because
+/// different generic instantiations (e.g., `Vec<A>`, `Vec<B>`) share the same 
type ID.
 #[inline]
 fn check_generic_container_type(type_info: &TypeInfo) -> Result<(), Error> {
     let type_id = type_info.get_type_id();


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

Reply via email to