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 b1176610e feat(rust): support enum variant for schema evolution mode 
(#2873)
b1176610e is described below

commit b1176610e7ce3374786d0423c023a0c03e1752e6
Author: Shawn Yang <[email protected]>
AuthorDate: Mon Nov 3 00:05:35 2025 +0800

    feat(rust): support enum variant for schema evolution mode (#2873)
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    
    ## What does this PR do?
    This PR add Compatible mode for enum for all three neum types.
    
    For struct like enum variant, use same protocol as struct compatible.
    For tuple like enum variant, use same protocol as tuple compatible.
    
    Compatible mode enables robust schema evolution with variant type
    encoding (2 bits):
    
    - `0b0` = Unit, `0b1` = Unnamed, `0b10` = Named
    
    ```rust
    use fory::{Fory, ForyObject};
    
    // Old version
    #[derive(ForyObject)]
    enum OldEvent {
        Click { x: i32, y: i32 },
        Scroll { delta: f64 },
    }
    
    // New version - added field and variant
    #[derive(Default, ForyObject)]
    enum NewEvent {
        #[default]
        Unknown,
        Click { x: i32, y: i32, timestamp: u64 },  // Added field
        Scroll { delta: f64 },
        KeyPress(String),  // New variant
    }
    
    let mut fory = Fory::builder().compatible().build();
    
    // Serialize with old schema
    let old_bytes = fory.serialize(&OldEvent::Click { x: 100, y: 200 })?;
    
    // Deserialize with new schema - timestamp gets default value (0)
    let new_event: NewEvent = fory.deserialize(&old_bytes)?;
    assert!(matches!(new_event, NewEvent::Click { x: 100, y: 200, timestamp: 0 
}));
    ```
    
    **Evolution capabilities:**
    
    - **Unknown variants** → Falls back to default variant
    - **Named variant fields** → Add/remove fields (missing fields use
    defaults)
    - **Unnamed variant elements** → Add/remove elements (extras skipped,
    missing use defaults)
    - **Variant type mismatches** → Automatically uses default variant
    
    
    ## Related issues
    
    #2861
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 rust/README.md                               |  86 +++-
 rust/fory-core/src/meta/mod.rs               |   3 +-
 rust/fory-core/src/meta/type_meta.rs         |  45 ++
 rust/fory-core/src/resolver/context.rs       |   5 +
 rust/fory-core/src/resolver/type_resolver.rs | 459 +++++++++--------
 rust/fory-core/src/serializer/core.rs        |   9 +-
 rust/fory-core/src/serializer/enum_.rs       |  13 +
 rust/fory-core/src/serializer/skip.rs        |  54 ++
 rust/fory-derive/src/object/derive_enum.rs   | 502 ++++++++++++++++--
 rust/fory-derive/src/object/misc.rs          |   8 +-
 rust/fory-derive/src/object/read.rs          |  34 +-
 rust/fory-derive/src/object/serializer.rs    |  56 +-
 rust/fory/src/lib.rs                         | 101 +++-
 rust/tests/tests/test_enum_compatible.rs     | 735 +++++++++++++++++++++++++++
 14 files changed, 1791 insertions(+), 319 deletions(-)

diff --git a/rust/README.md b/rust/README.md
index ca48cbb3c..c2d58a68b 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -559,36 +559,92 @@ assert_eq!(person_v2.phone, None);
 
 ### 5. Enum Support
 
-Apache Fory™ supports enums without data payloads (C-style enums). Each 
variant is assigned an ordinal value (0, 1, 2, ...) during serialization.
+Apache Fory™ supports three types of enum variants with full schema evolution 
in Compatible mode:
+
+**Variant Types:**
+
+- **Unit**: C-style enums (`Status::Active`)
+- **Unnamed**: Tuple-like variants (`Message::Pair(String, i32)`)
+- **Named**: Struct-like variants (`Event::Click { x: i32, y: i32 }`)
 
 **Features:**
 
-- Efficient varint encoding for ordinals
-- Schema evolution support in Compatible mode
-- Type-safe variant matching
+- Efficient varint encoding for variant ordinals
+- Schema evolution support (add/remove variants, add/remove fields)
 - Default variant support with `#[default]`
+- Automatic type mismatch handling
 
 ```rust
-use fory::ForyObject;
+use fory::{Fory, ForyObject};
 
 #[derive(Default, ForyObject, Debug, PartialEq)]
-enum Status {
+enum Value {
     #[default]
-    Pending,
-    Active,
-    Inactive,
-    Deleted,
+    Null,
+    Bool(bool),
+    Number(f64),
+    Text(String),
+    Object { name: String, value: i32 },
 }
 
 let mut fory = Fory::default();
-fory.register::<Status>(1);
+fory.register::<Value>(1)?;
+
+let value = Value::Object { name: "score".to_string(), value: 100 };
+let bytes = fory.serialize(&value)?;
+let decoded: Value = fory.deserialize(&bytes)?;
+assert_eq!(value, decoded);
+```
+
+#### Schema Evolution
+
+Compatible mode enables robust schema evolution with variant type encoding (2 
bits):
+
+- `0b0` = Unit, `0b1` = Unnamed, `0b10` = Named
+
+```rust
+use fory::{Fory, ForyObject};
+
+// Old version
+#[derive(ForyObject)]
+enum OldEvent {
+    Click { x: i32, y: i32 },
+    Scroll { delta: f64 },
+}
+
+// New version - added field and variant
+#[derive(Default, ForyObject)]
+enum NewEvent {
+    #[default]
+    Unknown,
+    Click { x: i32, y: i32, timestamp: u64 },  // Added field
+    Scroll { delta: f64 },
+    KeyPress(String),  // New variant
+}
+
+let mut fory = Fory::builder().compatible().build();
 
-let status = Status::Active;
-let bytes = fory.serialize(&status);
-let decoded: Status = fory.deserialize(&bytes)?;
-assert_eq!(status, decoded);
+// Serialize with old schema
+let old_bytes = fory.serialize(&OldEvent::Click { x: 100, y: 200 })?;
+
+// Deserialize with new schema - timestamp gets default value (0)
+let new_event: NewEvent = fory.deserialize(&old_bytes)?;
+assert!(matches!(new_event, NewEvent::Click { x: 100, y: 200, timestamp: 0 }));
 ```
 
+**Evolution capabilities:**
+
+- **Unknown variants** → Falls back to default variant
+- **Named variant fields** → Add/remove fields (missing fields use defaults)
+- **Unnamed variant elements** → Add/remove elements (extras skipped, missing 
use defaults)
+- **Variant type mismatches** → Automatically uses default value for current 
variant
+
+**Best practices:**
+
+- Always mark a default variant with `#[default]`
+- Named variants provide better evolution than unnamed
+- Use compatible mode for cross-version communication
+
 ### 6. Tuple Support
 
 Apache Fory™ supports tuples up to 22 elements out of the box with efficient 
serialization in both compatible and non-compatible modes.
diff --git a/rust/fory-core/src/meta/mod.rs b/rust/fory-core/src/meta/mod.rs
index ce7bd276a..ce68ff186 100644
--- a/rust/fory-core/src/meta/mod.rs
+++ b/rust/fory-core/src/meta/mod.rs
@@ -25,5 +25,6 @@ pub use meta_string::{
 };
 pub use string_util::{buffer_rw_string, get_latin1_length, is_latin, 
murmurhash3_x64_128};
 pub use type_meta::{
-    FieldInfo, FieldType, TypeMeta, TypeMetaLayer, NAMESPACE_ENCODINGS, 
TYPE_NAME_ENCODINGS,
+    sort_fields, FieldInfo, FieldType, TypeMeta, TypeMetaLayer, 
NAMESPACE_ENCODINGS,
+    TYPE_NAME_ENCODINGS,
 };
diff --git a/rust/fory-core/src/meta/type_meta.rs 
b/rust/fory-core/src/meta/type_meta.rs
index d105b0344..9b58a05ef 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -235,6 +235,50 @@ impl FieldInfo {
     }
 }
 
+/// Sorts field infos according to the provided sorted field names and assigns 
field IDs.
+///
+/// This function takes a vector of field infos and a slice of sorted field 
names,
+/// then reorders the field infos to match the sorted order and assigns 
sequential
+/// field IDs starting from 0.
+///
+/// # Arguments
+///
+/// * `fields_info` - A mutable vector of FieldInfo to be sorted and assigned 
IDs
+/// * `sorted_field_names` - A slice of field names in the desired sorted order
+///
+/// # Errors
+///
+/// Returns an error if a field name in `sorted_field_names` is not found in 
`fields_info`
+pub fn sort_fields(
+    fields_info: &mut Vec<FieldInfo>,
+    sorted_field_names: &[&str],
+) -> Result<(), Error> {
+    let mut sorted_field_infos: Vec<FieldInfo> = 
Vec::with_capacity(fields_info.len());
+    for name in sorted_field_names.iter() {
+        let mut found = false;
+        for i in 0..fields_info.len() {
+            if &fields_info[i].field_name == name {
+                // swap_remove is faster
+                sorted_field_infos.push(fields_info.swap_remove(i));
+                found = true;
+                break;
+            }
+        }
+        if !found {
+            return Err(Error::type_error(format!(
+                "Field {} not found in fields_info",
+                name
+            )));
+        }
+    }
+    // assign field id in ascending order
+    for (i, field_info) in sorted_field_infos.iter_mut().enumerate() {
+        field_info.field_id = i as i16;
+    }
+    *fields_info = sorted_field_infos;
+    Ok(())
+}
+
 impl PartialEq for FieldType {
     fn eq(&self, other: &Self) -> bool {
         self.type_id == other.type_id && self.generics == other.generics
@@ -604,6 +648,7 @@ impl TypeMeta {
         }
     }
 
+    #[allow(dead_code)]
     pub(crate) fn from_bytes(
         reader: &mut Reader,
         type_resolver: &TypeResolver,
diff --git a/rust/fory-core/src/resolver/context.rs 
b/rust/fory-core/src/resolver/context.rs
index 737013cd1..c116d6666 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -336,6 +336,11 @@ impl<'a> ReadContext<'a> {
         })
     }
 
+    #[inline(always)]
+    pub fn get_meta(&self, type_index: usize) -> Result<&Rc<TypeInfo>, Error> {
+        self.get_type_info_by_index(type_index)
+    }
+
     #[inline(always)]
     pub fn load_type_meta(&mut self, offset: usize) -> Result<usize, Error> {
         self.meta_resolver.load(
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index e1faf81ed..2278a401c 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -18,12 +18,12 @@
 use super::context::{ReadContext, WriteContext};
 use crate::error::Error;
 use crate::meta::{
-    FieldInfo, MetaString, TypeMeta, NAMESPACE_ENCODER, NAMESPACE_ENCODINGS, 
TYPE_NAME_ENCODER,
+    MetaString, TypeMeta, NAMESPACE_ENCODER, NAMESPACE_ENCODINGS, 
TYPE_NAME_ENCODER,
     TYPE_NAME_ENCODINGS,
 };
 use crate::serializer::{ForyDefault, Serializer, StructSerializer};
 use crate::util::get_ext_actual_type_id;
-use crate::{Reader, TypeId};
+use crate::TypeId;
 use chrono::{NaiveDate, NaiveDateTime};
 use std::collections::{HashSet, LinkedList};
 use std::rc::Rc;
@@ -44,7 +44,7 @@ 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 ToSerializerFn = fn(Box<dyn Any>) -> Result<Box<dyn Serializer>, Error>;
-type GetSortedFieldInfosFn = fn(&TypeResolver) -> Result<Vec<FieldInfo>, 
Error>;
+type BuildTypeInfosFn = fn(&TypeResolver) -> Result<Vec<(std::any::TypeId, 
TypeInfo)>, Error>;
 const EMPTY_STRING: String = String::new();
 
 #[derive(Clone, Debug)]
@@ -54,7 +54,7 @@ pub struct Harness {
     write_data_fn: WriteDataFn,
     read_data_fn: ReadDataFn,
     to_serializer: ToSerializerFn,
-    sorted_field_infos: GetSortedFieldInfosFn,
+    build_type_infos: BuildTypeInfosFn,
 }
 
 impl Harness {
@@ -64,7 +64,7 @@ impl Harness {
         write_data_fn: WriteDataFn,
         read_data_fn: ReadDataFn,
         to_serializer: ToSerializerFn,
-        sorted_field_infos: GetSortedFieldInfosFn,
+        build_type_infos: BuildTypeInfosFn,
     ) -> Harness {
         Harness {
             write_fn,
@@ -72,10 +72,21 @@ impl Harness {
             write_data_fn,
             read_data_fn,
             to_serializer,
-            sorted_field_infos,
+            build_type_infos,
         }
     }
 
+    pub fn stub() -> Harness {
+        Harness::new(
+            stub_write_fn,
+            stub_read_fn,
+            stub_write_data_fn,
+            stub_read_data_fn,
+            stub_to_serializer_fn,
+            stub_build_type_infos,
+        )
+    }
+
     #[inline(always)]
     pub fn get_write_fn(&self) -> WriteFn {
         self.write_fn
@@ -115,37 +126,6 @@ pub struct TypeInfo {
 
 impl TypeInfo {
     fn new(
-        type_resolver: &TypeResolver,
-        type_id: u32,
-        namespace: &str,
-        type_name: &str,
-        register_by_name: bool,
-        harness: Harness,
-    ) -> Result<TypeInfo, Error> {
-        let namespace_meta_string =
-            NAMESPACE_ENCODER.encode_with_encodings(namespace, 
NAMESPACE_ENCODINGS)?;
-        let type_name_meta_string =
-            TYPE_NAME_ENCODER.encode_with_encodings(type_name, 
TYPE_NAME_ENCODINGS)?;
-        let type_meta = Rc::new(TypeMeta::from_fields(
-            type_id,
-            namespace_meta_string.clone(),
-            type_name_meta_string.clone(),
-            register_by_name,
-            (harness.sorted_field_infos)(type_resolver)?,
-        ));
-        let type_def_bytes = type_meta.to_bytes()?;
-        Ok(TypeInfo {
-            type_def: Rc::from(type_def_bytes),
-            type_meta,
-            type_id,
-            namespace: Rc::from(namespace_meta_string),
-            type_name: Rc::from(type_name_meta_string),
-            register_by_name,
-            harness,
-        })
-    }
-
-    fn new_lazy(
         type_id: u32,
         namespace: &str,
         type_name: &str,
@@ -167,33 +147,18 @@ impl TypeInfo {
         })
     }
 
-    fn new_with_empty_fields(
-        type_resolver: &TypeResolver,
-        type_id: u32,
-        namespace: &str,
-        type_name: &str,
-        register_by_name: bool,
-        harness: Harness,
-    ) -> Result<TypeInfo, Error> {
-        let namespace_meta_string =
-            NAMESPACE_ENCODER.encode_with_encodings(namespace, 
NAMESPACE_ENCODINGS)?;
-        let type_name_meta_string =
-            TYPE_NAME_ENCODER.encode_with_encodings(type_name, 
TYPE_NAME_ENCODINGS)?;
-        let meta = TypeMeta::from_fields(
-            type_id,
-            namespace_meta_string.clone(),
-            type_name_meta_string.clone(),
-            register_by_name,
-            vec![],
-        );
-        let type_def = meta.to_bytes()?;
-        let meta = TypeMeta::from_bytes(&mut Reader::new(&type_def), 
type_resolver)?;
+    fn new_with_type_meta(type_meta: Rc<TypeMeta>, harness: Harness) -> 
Result<TypeInfo, Error> {
+        let type_id = type_meta.get_type_id();
+        let namespace = type_meta.get_namespace();
+        let type_name = type_meta.get_type_name();
+        let register_by_name = !namespace.original.is_empty() || 
!type_name.original.is_empty();
+        let type_def_bytes = type_meta.to_bytes()?;
         Ok(TypeInfo {
-            type_def: Rc::from(type_def),
-            type_meta: Rc::new(meta),
+            type_def: Rc::from(type_def_bytes),
+            type_meta,
             type_id,
-            namespace: Rc::from(namespace_meta_string),
-            type_name: Rc::from(type_name_meta_string),
+            namespace,
+            type_name,
             register_by_name,
             harness,
         })
@@ -256,7 +221,7 @@ impl TypeInfo {
                 stub_write_data_fn,
                 stub_read_data_fn,
                 stub_to_serializer_fn,
-                stub_sorted_field_infos,
+                stub_build_type_infos,
             )
         };
 
@@ -309,12 +274,132 @@ fn stub_to_serializer_fn(_: Box<dyn Any>) -> 
Result<Box<dyn Serializer>, Error>
     ))
 }
 
-fn stub_sorted_field_infos(_: &TypeResolver) -> Result<Vec<FieldInfo>, Error> {
+fn stub_build_type_infos(_: &TypeResolver) -> Result<Vec<(std::any::TypeId, 
TypeInfo)>, Error> {
     Err(Error::type_error(
-        "Cannot get field infos for unknown remote type",
+        "Cannot get type infos for unknown remote type",
     ))
 }
 
+/// Helper function to build type infos for struct types
+fn build_struct_type_infos<T: StructSerializer>(
+    type_resolver: &TypeResolver,
+) -> Result<Vec<(std::any::TypeId, TypeInfo)>, Error> {
+    let partial_info = type_resolver
+        .partial_type_infos
+        .get(&std::any::TypeId::of::<T>())
+        .ok_or_else(|| Error::type_error("Partial type info not found for 
struct"))?;
+
+    // Get sorted field infos (fields are already sorted and have IDs assigned 
by the macro)
+    let sorted_field_infos = T::fory_fields_info(type_resolver)?;
+
+    // Build the main type info
+    let type_meta = TypeMeta::from_fields(
+        partial_info.type_id,
+        (*partial_info.namespace).clone(),
+        (*partial_info.type_name).clone(),
+        partial_info.register_by_name,
+        sorted_field_infos,
+    );
+    let type_def_bytes = type_meta.to_bytes()?;
+    let main_type_info = TypeInfo {
+        type_def: Rc::from(type_def_bytes),
+        type_meta: Rc::new(type_meta),
+        type_id: partial_info.type_id,
+        namespace: partial_info.namespace.clone(),
+        type_name: partial_info.type_name.clone(),
+        register_by_name: partial_info.register_by_name,
+        harness: partial_info.harness.clone(),
+    };
+
+    let mut result = vec![(std::any::TypeId::of::<T>(), main_type_info)];
+
+    // Handle enum variants in compatible mode
+    if type_resolver.compatible && T::fory_static_type_id() == TypeId::ENUM {
+        // Fields are already sorted with IDs assigned by the macro
+        let variants_info = T::fory_variants_fields_info(type_resolver)?;
+        for (idx, (variant_name, variant_type_id, fields_info)) in
+            variants_info.into_iter().enumerate()
+        {
+            // Skip empty variant info (unit/unnamed variants)
+            if fields_info.is_empty() {
+                continue;
+            }
+            // Create TypeMeta for the variant
+            let variant_type_meta = if partial_info.register_by_name {
+                let variant_type_name =
+                    format!("{}_{}", partial_info.type_name.original, 
variant_name);
+                let namespace_ms = NAMESPACE_ENCODER
+                    .encode_with_encodings(&partial_info.namespace.original, 
NAMESPACE_ENCODINGS)?;
+                let type_name_ms = TYPE_NAME_ENCODER
+                    .encode_with_encodings(&variant_type_name, 
TYPE_NAME_ENCODINGS)?;
+                TypeMeta::from_fields(
+                    TypeId::ENUM as u32,
+                    namespace_ms,
+                    type_name_ms,
+                    true,
+                    fields_info.clone(),
+                )
+            } else {
+                // add a check to avoid collision with main enum type_id
+                // since internal id is big alealdy, `74<<8 = 18944` is big 
enough to avoid collision most of time
+                let variant_id = (partial_info.type_id << 8) + idx as u32;
+
+                // Check if variant_id conflicts with any already registered 
type
+                if let Some(existing_info) = 
type_resolver.type_info_map_by_id.get(&variant_id) {
+                    return Err(Error::type_error(format!(
+                        "Enum variant type ID {} (calculated from enum type ID 
{} with variant index {}) conflicts with already registered type ID {}. \
+                         Please use a different type ID for the enum to avoid 
conflicts.",
+                        variant_id, partial_info.type_id, idx, 
existing_info.type_id
+                    )));
+                }
+
+                TypeMeta::from_fields(
+                    variant_id,
+                    MetaString::get_empty().clone(),
+                    MetaString::get_empty().clone(),
+                    false,
+                    fields_info,
+                )
+            };
+
+            let variant_type_info =
+                TypeInfo::new_with_type_meta(Rc::new(variant_type_meta), 
Harness::stub())?;
+
+            // Store the variant type_id with its TypeId
+            result.push((variant_type_id, variant_type_info));
+        }
+    }
+
+    Ok(result)
+}
+
+/// Helper function to build type infos for serializer types (ext types)
+fn build_serializer_type_infos(
+    partial_info: &TypeInfo,
+    rust_type_id: std::any::TypeId,
+) -> Result<Vec<(std::any::TypeId, TypeInfo)>, Error> {
+    // For ext types, we just build the type info with empty fields
+    let type_meta = TypeMeta::from_fields(
+        partial_info.type_id,
+        (*partial_info.namespace).clone(),
+        (*partial_info.type_name).clone(),
+        partial_info.register_by_name,
+        vec![],
+    );
+    let type_def_bytes = type_meta.to_bytes()?;
+    let type_info = TypeInfo {
+        type_def: Rc::from(type_def_bytes),
+        type_meta: Rc::new(type_meta),
+        type_id: partial_info.type_id,
+        namespace: partial_info.namespace.clone(),
+        type_name: partial_info.type_name.clone(),
+        register_by_name: partial_info.register_by_name,
+        harness: partial_info.harness.clone(),
+    };
+
+    Ok(vec![(rust_type_id, type_info)])
+}
+
 /// TypeResolver is a resolver for fast type/serializer dispatch.
 pub struct TypeResolver {
     type_info_map_by_id: HashMap<u32, Rc<TypeInfo>>,
@@ -353,13 +438,14 @@ impl Default for TypeResolver {
 
 impl TypeResolver {
     pub fn get_type_info(&self, type_id: &std::any::TypeId) -> 
Result<Rc<TypeInfo>, Error> {
-        self.type_info_map.get(type_id)
-                .ok_or_else(|| {
-                    Error::type_error(format!(
-                        "TypeId {:?} not found in type_info registry, maybe 
you forgot to register some types",
-                        type_id
-                    ))
-                })
+        self.type_info_map
+            .get(type_id)
+            .ok_or_else(|| {
+                Error::type_error(format!(
+                    "TypeId {:?} not found in type_info registry, maybe you 
forgot to register some types",
+                    type_id
+                ))
+            })
             .cloned()
     }
 
@@ -511,7 +597,7 @@ impl TypeResolver {
         id: u32,
         namespace: &str,
         type_name: &str,
-        lazy: bool,
+        _lazy: bool,
     ) -> Result<(), Error> {
         let register_by_name = !type_name.is_empty();
         if !register_by_name && id == 0 {
@@ -587,31 +673,10 @@ impl TypeResolver {
             }
         }
 
-        fn sorted_field_infos<T: StructSerializer>(
+        fn build_type_infos<T: StructSerializer>(
             type_resolver: &TypeResolver,
-        ) -> Result<Vec<FieldInfo>, Error> {
-            let mut fields_info = T::fory_fields_info(type_resolver)?;
-            let sorted_field_names = T::fory_get_sorted_field_names();
-            let mut sorted_field_infos: Vec<FieldInfo> = 
Vec::with_capacity(fields_info.len());
-            for name in sorted_field_names.iter() {
-                let mut found = false;
-                for i in 0..fields_info.len() {
-                    if &fields_info[i].field_name == name {
-                        // swap_remove is faster
-                        sorted_field_infos.push(fields_info.swap_remove(i));
-                        found = true;
-                        break;
-                    }
-                }
-                if !found {
-                    unreachable!("Field {} not found in fields_info", name);
-                }
-            }
-            // assign field id in ascending order
-            for (i, field_info) in sorted_field_infos.iter_mut().enumerate() {
-                field_info.field_id = i as i16;
-            }
-            Ok(sorted_field_infos)
+        ) -> Result<Vec<(std::any::TypeId, TypeInfo)>, Error> {
+            build_struct_type_infos::<T>(type_resolver)
         }
 
         let harness = Harness::new(
@@ -620,44 +685,37 @@ impl TypeResolver {
             write_data::<T>,
             read_data::<T>,
             to_serializer::<T>,
-            sorted_field_infos::<T>,
+            build_type_infos::<T>,
         );
-        let type_info = if lazy {
-            let type_info = TypeInfo::new_lazy(
-                actual_type_id,
-                namespace,
-                type_name,
-                register_by_name,
-                harness,
-            )?;
-            self.partial_type_infos
-                .insert(std::any::TypeId::of::<T>(), type_info.clone());
-            type_info
-        } else {
-            TypeInfo::new(
-                self,
-                actual_type_id,
-                namespace,
-                type_name,
-                register_by_name,
-                harness,
-            )?
-        };
+        let type_info = TypeInfo::new(
+            actual_type_id,
+            namespace,
+            type_name,
+            register_by_name,
+            harness,
+        )?;
+
         let rs_type_id = std::any::TypeId::of::<T>();
-        if self.type_info_map.contains_key(&rs_type_id) {
+        if self.partial_type_infos.contains_key(&rs_type_id) {
             return Err(Error::type_error(format!(
                 "rs_struct:{:?} already registered",
                 rs_type_id
             )));
         }
 
-        // Store in main map
-        self.type_info_map
-            .insert(rs_type_id, Rc::new(type_info.clone()));
-
-        // Store by ID
-        self.type_info_map_by_id
-            .insert(type_info.type_id, Rc::new(type_info.clone()));
+        // Check if type_id conflicts with any already registered type
+        // Skip check for:
+        // 1. Internal types (type_id <= TypeId::UNKNOWN) as they can be shared
+        // 2. Types registered by name (they use shared type IDs like 
NAMED_STRUCT)
+        if !register_by_name
+            && actual_type_id > TypeId::UNKNOWN as u32
+            && self.type_info_map_by_id.contains_key(&actual_type_id)
+        {
+            return Err(Error::type_error(format!(
+                "Type ID {} conflicts with already registered type. Please use 
a different type ID.",
+                actual_type_id
+            )));
+        }
 
         // Update type_id_index for fast lookup
         let index = T::fory_type_index() as usize;
@@ -671,23 +729,13 @@ impl TypeResolver {
         }
         self.type_id_index[index] = type_info.type_id;
 
-        // Store by name if registered by name
-        if type_info.register_by_name {
-            let namespace = &type_info.namespace;
-            let type_name = &type_info.type_name;
-            let ms_key = (namespace.clone(), type_name.clone());
-            if self.type_info_map_by_meta_string_name.contains_key(&ms_key) {
-                return Err(Error::invalid_data(format!(
-                    "Namespace:{:?} Name:{:?} already registered_by_name",
-                    namespace, type_name
-                )));
-            }
-            self.type_info_map_by_meta_string_name
-                .insert(ms_key, Rc::new(type_info.clone()));
-            let string_key = (namespace.original.clone(), 
type_name.original.clone());
-            self.type_info_map_by_name
-                .insert(string_key, Rc::new(type_info));
-        }
+        // Insert partial type info into both maps
+        self.type_info_map_by_id
+            .insert(actual_type_id, Rc::new(type_info.clone()));
+        self.type_info_map
+            .insert(rs_type_id, Rc::new(type_info.clone()));
+        self.partial_type_infos.insert(rs_type_id, type_info);
+
         Ok(())
     }
 
@@ -807,8 +855,14 @@ impl TypeResolver {
             }
         }
 
-        fn sorted_field_infos(_: &TypeResolver) -> Result<Vec<FieldInfo>, 
Error> {
-            Ok(vec![])
+        fn build_type_infos<T2: 'static>(
+            type_resolver: &TypeResolver,
+        ) -> Result<Vec<(std::any::TypeId, TypeInfo)>, Error> {
+            let partial_info = type_resolver
+                .partial_type_infos
+                .get(&std::any::TypeId::of::<T2>())
+                .ok_or_else(|| Error::type_error("Partial type info not found 
for serializer"))?;
+            build_serializer_type_infos(partial_info, 
std::any::TypeId::of::<T2>())
         }
 
         let harness = Harness::new(
@@ -817,11 +871,10 @@ impl TypeResolver {
             write_data::<T>,
             read_data::<T>,
             to_serializer::<T>,
-            sorted_field_infos,
+            build_type_infos::<T>,
         );
 
-        let type_info = TypeInfo::new_with_empty_fields(
-            self,
+        let type_info = TypeInfo::new(
             actual_type_id,
             namespace,
             type_name,
@@ -830,38 +883,30 @@ impl TypeResolver {
         )?;
 
         let rs_type_id = std::any::TypeId::of::<T>();
-        if self.type_info_map.contains_key(&rs_type_id) {
+        if self.partial_type_infos.contains_key(&rs_type_id) {
             return Err(Error::type_error(format!(
                 "rs_struct:{:?} already registered",
                 rs_type_id
             )));
         }
 
-        // Store in main map
-        self.type_info_map
-            .insert(rs_type_id, Rc::new(type_info.clone()));
+        // Check if type_id conflicts with any already registered type
+        // Skip check for internal types (type_id <= TypeId::UNKNOWN) as they 
can be shared
+        if actual_type_id > TypeId::UNKNOWN as u32
+            && self.type_info_map_by_id.contains_key(&actual_type_id)
+        {
+            return Err(Error::type_error(format!(
+                "Type ID {} conflicts with already registered type. Please use 
a different type ID.",
+                actual_type_id
+            )));
+        }
 
-        // Store by ID
+        // Insert partial type info into both maps
         self.type_info_map_by_id
-            .insert(type_info.type_id, Rc::new(type_info.clone()));
-
-        // Store by name if registered by name
-        if type_info.register_by_name {
-            let namespace = &type_info.namespace;
-            let type_name = &type_info.type_name;
-            let ms_key = (namespace.clone(), type_name.clone());
-            if self.type_info_map_by_meta_string_name.contains_key(&ms_key) {
-                return Err(Error::invalid_data(format!(
-                    "Namespace:{:?} Name:{:?} already registered_by_name",
-                    namespace, type_name
-                )));
-            }
-            self.type_info_map_by_meta_string_name
-                .insert(ms_key, Rc::new(type_info.clone()));
-            let string_key = (namespace.original.clone(), 
type_name.original.clone());
-            self.type_info_map_by_name
-                .insert(string_key, Rc::new(type_info));
-        }
+            .insert(actual_type_id, Rc::new(type_info.clone()));
+        self.type_info_map
+            .insert(rs_type_id, Rc::new(type_info.clone()));
+        self.partial_type_infos.insert(rs_type_id, type_info);
         Ok(())
     }
 
@@ -915,49 +960,41 @@ impl TypeResolver {
         let mut type_info_map = self.type_info_map.clone();
         let mut type_info_map_by_name = self.type_info_map_by_name.clone();
         let mut type_info_map_by_meta_string_name = 
self.type_info_map_by_meta_string_name.clone();
+        let type_id_index = self.type_id_index.clone();
+
         // Iterate over partial_type_infos and complete them
-        for (type_id, partial_type_info) in self.partial_type_infos.iter() {
-            let harness = partial_type_info.harness.clone();
-            let sorted_field_infos = (harness.sorted_field_infos)(self)?;
-            let type_meta = TypeMeta::from_fields(
-                partial_type_info.type_id,
-                (*partial_type_info.namespace).clone(),
-                (*partial_type_info.type_name).clone(),
-                partial_type_info.register_by_name,
-                sorted_field_infos,
-            );
-            let completed_type_info = TypeInfo {
-                type_def: Rc::from(type_meta.to_bytes()?),
-                type_meta: Rc::new(type_meta),
-                type_id: partial_type_info.type_id,
-                namespace: partial_type_info.namespace.clone(),
-                type_name: partial_type_info.type_name.clone(),
-                register_by_name: partial_type_info.register_by_name,
-                harness,
-            };
-            // Insert into all maps
-            type_info_map_by_id.insert(
-                completed_type_info.type_id,
-                Rc::new(completed_type_info.clone()),
-            );
-            type_info_map.insert(*type_id, 
Rc::new(completed_type_info.clone()));
-            if completed_type_info.register_by_name {
-                let namespace = &completed_type_info.namespace;
-                let type_name = &completed_type_info.type_name;
-                let ms_key = (namespace.clone(), type_name.clone());
-                type_info_map_by_meta_string_name
-                    .insert(ms_key, Rc::new(completed_type_info.clone()));
-                let string_key = (namespace.original.clone(), 
type_name.original.clone());
-                type_info_map_by_name.insert(string_key, 
Rc::new(completed_type_info));
+        for (_rust_type_id, partial_type_info) in 
self.partial_type_infos.iter() {
+            let harness = &partial_type_info.harness;
+            // Call build_type_infos to get all type infos (main + enum 
variants)
+            let type_infos = (harness.build_type_infos)(self)?;
+
+            // Iterate through all type infos uniformly
+            for (type_rust_id, type_info) in type_infos.iter() {
+                // Insert into type_info_map_by_id
+                type_info_map_by_id.insert(type_info.type_id, 
Rc::new(type_info.clone()));
+
+                // Insert into type_info_map with the TypeId
+                type_info_map.insert(*type_rust_id, 
Rc::new(type_info.clone()));
+
+                // Insert into name-based maps if registered by name
+                if type_info.register_by_name {
+                    let namespace = &type_info.namespace;
+                    let type_name = &type_info.type_name;
+                    let ms_key = (namespace.clone(), type_name.clone());
+                    type_info_map_by_meta_string_name.insert(ms_key, 
Rc::new(type_info.clone()));
+                    let string_key = (namespace.original.clone(), 
type_name.original.clone());
+                    type_info_map_by_name.insert(string_key, 
Rc::new(type_info.clone()));
+                }
             }
         }
+
         Ok(TypeResolver {
-            type_info_map_by_id: type_info_map_by_id.clone(),
-            type_info_map: type_info_map.clone(),
-            type_info_map_by_name: type_info_map_by_name.clone(),
-            type_info_map_by_meta_string_name: 
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(),
+            type_id_index,
             compatible: self.compatible,
         })
     }
diff --git a/rust/fory-core/src/serializer/core.rs 
b/rust/fory-core/src/serializer/core.rs
index 9e17b6d04..128119f0e 100644
--- a/rust/fory-core/src/serializer/core.rs
+++ b/rust/fory-core/src/serializer/core.rs
@@ -142,7 +142,7 @@ pub trait ForyDefault: Sized {
 /// - [`fory_static_type_id`]: Static type ID (defaults to TypeId::EXT for 
user types)
 /// - [`fory_get_type_id`]: Get registered type ID from TypeResolver
 /// - [`fory_concrete_type_id`]: Get Rust's std::any::TypeId
-/// - [`fory_is_option`]: Check if type is Option<T>
+/// - [`fory_is_option`]: Check if type is `Option<T>`
 /// - [`fory_is_none`]: Check if instance is None (for Option types)
 /// - [`fory_is_polymorphic`]: Check if type supports polymorphism
 /// - [`fory_is_shared_ref`]: Check if type is Rc/Arc
@@ -1338,6 +1338,13 @@ pub trait StructSerializer: Serializer + 'static {
         Ok(Vec::default())
     }
 
+    #[allow(unused_variables)]
+    fn fory_variants_fields_info(
+        type_resolver: &TypeResolver,
+    ) -> Result<Vec<(String, std::any::TypeId, Vec<FieldInfo>)>, Error> {
+        Ok(Vec::default())
+    }
+
     /// Get the type index for fast struct type lookup.
     ///
     /// Type index provides O(1) lookup for struct types in the type registry.
diff --git a/rust/fory-core/src/serializer/enum_.rs 
b/rust/fory-core/src/serializer/enum_.rs
index 46755f210..1c615619c 100644
--- a/rust/fory-core/src/serializer/enum_.rs
+++ b/rust/fory-core/src/serializer/enum_.rs
@@ -17,9 +17,11 @@
 
 use crate::ensure;
 use crate::error::Error;
+use crate::meta::FieldInfo;
 use crate::resolver::context::{ReadContext, WriteContext};
 use crate::serializer::{ForyDefault, Serializer};
 use crate::types::{RefFlag, TypeId};
+use crate::TypeResolver;
 
 #[inline(always)]
 pub fn actual_type_id(type_id: u32, register_by_name: bool, _compatible: bool) 
-> u32 {
@@ -116,3 +118,14 @@ pub fn read_type_info<T: Serializer>(context: &mut 
ReadContext) -> Result<(), Er
     }
     Ok(())
 }
+
+pub trait NamedEnumVariantMetaTrait: 'static {
+    fn fory_get_sorted_field_names() -> &'static [&'static str] {
+        &[]
+    }
+
+    #[allow(unused_variables)]
+    fn fory_fields_info(type_resolver: &TypeResolver) -> 
Result<Vec<FieldInfo>, Error> {
+        Ok(Vec::default())
+    }
+}
diff --git a/rust/fory-core/src/serializer/skip.rs 
b/rust/fory-core/src/serializer/skip.rs
index 17e2cd0ae..89f013264 100644
--- a/rust/fory-core/src/serializer/skip.rs
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -464,3 +464,57 @@ fn skip_value(
     }
     Ok(())
 }
+
+/// Skip enum variant data in compatible mode based on variant type.
+///
+/// # Arguments
+/// * `context` - The read context
+/// * `variant_type` - The variant type encoded in lower 2 bits:
+///   - 0b0 = Unit variant (no data to skip)
+///   - 0b1 = Unnamed variant (tuple data)
+///   - 0b10 = Named variant (struct-like data)
+/// * `type_info` - Optional type info for named variants (must be provided 
for 0b10)
+pub fn skip_enum_variant(
+    context: &mut ReadContext,
+    variant_type: u32,
+    type_info: &Option<Rc<crate::TypeInfo>>,
+) -> Result<(), Error> {
+    match variant_type {
+        0b0 => {
+            // Unit variant, no data to skip
+            Ok(())
+        }
+        0b1 => {
+            // Unnamed variant, skip tuple data (which is serialized as a 
collection)
+            // Tuple uses collection format but doesn't write type info, so 
skip directly
+            let field_type = FieldType {
+                type_id: types::LIST,
+                nullable: false,
+                generics: vec![UNKNOWN_FIELD_TYPE],
+            };
+            skip_collection(context, &field_type)
+        }
+        0b10 => {
+            // Named variant, skip struct-like data using skip_struct
+            // For named variants, we need the type_info which should have 
been read already
+            if type_info.is_some() {
+                let type_id = type_info.as_ref().unwrap().get_type_id();
+                skip_struct(context, type_id, type_info)
+            } else {
+                // If no type_info provided, read it from the stream
+                let meta_index = context.reader.read_varuint32()?;
+                let type_info_rc = context.get_type_info_by_index(meta_index 
as usize)?.clone();
+                let type_id = type_info_rc.get_type_id();
+                let type_info_opt = Some(type_info_rc);
+                skip_struct(context, type_id, &type_info_opt)
+            }
+        }
+        _ => {
+            // Invalid variant type
+            Err(Error::type_error(format!(
+                "Invalid enum variant type: {}",
+                variant_type
+            )))
+        }
+    }
+}
diff --git a/rust/fory-derive/src/object/derive_enum.rs 
b/rust/fory-derive/src/object/derive_enum.rs
index 93462e2ca..b818fad1c 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -16,13 +16,14 @@
 // under the License.
 
 use super::util::{is_default_value_variant, is_skip_enum_variant};
+use crate::object::misc;
 use crate::object::read::gen_read_field;
-
+use crate::object::util::{get_filtered_fields_iter, get_sorted_field_names};
 use crate::object::write::gen_write_field;
+use crate::util::sorted_fields;
 use proc_macro2::{Ident, TokenStream};
 use quote::quote;
 use syn::{DataEnum, Fields};
-
 fn temp_var_name(i: usize) -> String {
     format!("f{}", i)
 }
@@ -39,25 +40,128 @@ pub fn gen_field_fields_info(_data_enum: &DataEnum) -> 
TokenStream {
     }
 }
 
+pub fn gen_variants_fields_info(enum_name: &syn::Ident, data_enum: &DataEnum) 
-> TokenStream {
+    let variant_info: Vec<TokenStream> = data_enum
+        .variants
+        .iter()
+        .map(|v| {
+            let variant_name = v.ident.to_string();
+            match &v.fields {
+                Fields::Named(_fields_named) => {
+                    // Generate meta type identifier for this named variant
+                    let meta_type_ident = Ident::new(
+                        &format!("{}_{}VariantMeta", enum_name, v.ident),
+                        proc_macro2::Span::call_site()
+                    );
+                    quote! {
+                        (
+                            #variant_name.to_string(),
+                            std::any::TypeId::of::<#meta_type_ident>(),
+                            <#meta_type_ident as 
fory_core::serializer::enum_::NamedEnumVariantMetaTrait>::fory_fields_info(type_resolver)?
+                        )
+                    }
+                }
+                _ => {
+                    // Unit or unnamed variants - return empty field info
+                    quote! {
+                        (
+                            #variant_name.to_string(),
+                            std::any::TypeId::of::<()>(), // Placeholder type 
ID
+                            Vec::new()
+                        )
+                    }
+                }
+            }
+        })
+        .collect();
+
+    quote! {
+        Ok(vec![
+            #(#variant_info),*
+        ])
+    }
+}
+
 pub fn gen_reserved_space() -> TokenStream {
     quote! {
        4
     }
 }
 
+/// Generate all variant meta types for an enum with the enum name
+pub(crate) fn gen_all_variant_meta_types_with_enum_name(
+    enum_name: &syn::Ident,
+    data_enum: &DataEnum,
+) -> Vec<TokenStream> {
+    data_enum
+        .variants
+        .iter()
+        .filter_map(|v| {
+            if let Fields::Named(fields_named) = &v.fields {
+                let ident = &v.ident;
+                Some(gen_named_variant_meta_type_impl_with_enum_name(
+                    enum_name,
+                    ident,
+                    fields_named,
+                ))
+            } else {
+                None
+            }
+        })
+        .collect()
+}
+
+/// Generate a meta type that implements NamedEnumVariantMetaTrait for a named 
variant
+/// with enum name to avoid collisions
+pub(crate) fn gen_named_variant_meta_type_impl_with_enum_name(
+    enum_ident: &Ident,
+    variant_ident: &Ident,
+    fields: &syn::FieldsNamed,
+) -> TokenStream {
+    let fields_clone = syn::Fields::Named(fields.clone());
+    let sorted_fields_slice = sorted_fields(&fields_clone);
+    let filtered_fields: Vec<_> = 
get_filtered_fields_iter(&sorted_fields_slice).collect();
+    let sorted_field_names_vec = get_sorted_field_names(&filtered_fields);
+
+    // Generate individual field name literals
+    let field_name_literals: Vec<_> = sorted_field_names_vec
+        .iter()
+        .map(|name| {
+            quote! { #name }
+        })
+        .collect();
+
+    let fields_info_ts = misc::gen_field_fields_info(&sorted_fields_slice);
+
+    // Include enum name to make meta type unique
+    let meta_type_ident = Ident::new(
+        &format!("{}_{}VariantMeta", enum_ident, variant_ident),
+        proc_macro2::Span::call_site(),
+    );
+
+    quote! {
+        struct #meta_type_ident;
+
+        impl fory_core::serializer::enum_::NamedEnumVariantMetaTrait for 
#meta_type_ident {
+            fn fory_get_sorted_field_names() -> &'static [&'static str] {
+                &[#(#field_name_literals),*]
+            }
+
+            fn fory_fields_info(type_resolver: &fory_core::TypeResolver) -> 
Result<Vec<fory_core::meta::FieldInfo>, fory_core::error::Error> {
+                #fields_info_ts
+            }
+        }
+    }
+}
+
 pub fn gen_write(_data_enum: &DataEnum) -> TokenStream {
     quote! {
         fory_core::serializer::enum_::write::<Self>(self, context, 
write_ref_info, write_type_info)
     }
 }
 
-pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
-    let default_variant_value = data_enum
-        .variants
-        .iter()
-        .position(is_default_value_variant)
-        .unwrap_or(0) as u32;
-    let xlang_variant_branches: Vec<TokenStream> = data_enum
+fn xlang_variant_branches(data_enum: &DataEnum, default_variant_value: u32) -> 
Vec<TokenStream> {
+    data_enum
         .variants
         .iter()
         .enumerate()
@@ -92,9 +196,11 @@ pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
                 }
             }
         })
-        .collect();
+        .collect()
+}
 
-    let rust_variant_branches: Vec<TokenStream> = data_enum
+fn rust_variant_branches(data_enum: &DataEnum, default_variant_value: u32) -> 
Vec<TokenStream> {
+    data_enum
         .variants
         .iter()
         .enumerate()
@@ -133,14 +239,10 @@ pub fn gen_write_data(data_enum: &DataEnum) -> 
TokenStream {
                     }
                 }
                 Fields::Named(fields_named) => {
-                    let mut sorted_fields: Vec<_> = 
fields_named.named.iter().collect();
-                    sorted_fields.sort_by(|a, b| {
-                        a.ident
-                            .as_ref()
-                            .unwrap()
-                            .to_string()
-                            .cmp(&b.ident.as_ref().unwrap().to_string())
-                    });
+                    use crate::util::sorted_fields;
+
+                    let fields_clone = 
syn::Fields::Named(fields_named.clone());
+                    let sorted_fields = sorted_fields(&fields_clone);
 
                     let field_idents: Vec<_> = sorted_fields
                         .iter()
@@ -162,7 +264,105 @@ pub fn gen_write_data(data_enum: &DataEnum) -> 
TokenStream {
                 }
             }
         })
-        .collect();
+        .collect()
+}
+
+fn rust_compatible_variant_write_branches(
+    data_enum: &DataEnum,
+    default_variant_value: u32,
+) -> Vec<TokenStream> {
+    use crate::object::util::get_struct_name;
+    let enum_name = get_struct_name().expect("enum context not set");
+
+    data_enum
+        .variants
+        .iter()
+        .enumerate()
+        .map(|(idx, v)| {
+            let ident = &v.ident;
+            let mut tag_value = idx as u32;
+            if is_skip_enum_variant(v) {
+                tag_value = default_variant_value;
+            }
+
+            match &v.fields {
+                Fields::Unit => {
+                    quote! {
+                        Self::#ident => {
+                            context.writer.write_varuint32((#tag_value << 2) | 
0b0);
+                        }
+                    }
+                }
+                Fields::Unnamed(fields_unnamed) => {
+                    // For unnamed enum variants, write using collection 
format (same protocol as tuple)
+                    let field_idents: Vec<_> = 
(0..fields_unnamed.unnamed.len())
+                        .map(|i| Ident::new(&temp_var_name(i), 
proc_macro2::Span::call_site()))
+                        .collect();
+
+                    let field_count = fields_unnamed.unnamed.len();
+
+                    quote! {
+                        Self::#ident( #(ref #field_idents),* ) => {
+                            context.writer.write_varuint32((#tag_value << 2) | 
0b1);
+                            // Write as collection format (same as tuple)
+                            context.writer.write_varuint32(#field_count as 
u32);
+                            let header = 0u8; // No IS_SAME_TYPE flag
+                            context.writer.write_u8(header);
+                            use fory_core::serializer::Serializer;
+                            #(
+                                #field_idents.fory_write(context, true, true, 
false)?;
+                            )*
+                        }
+                    }
+                }
+                Fields::Named(fields_named) => {
+                    // Generate meta type identifier for this named variant
+                    let meta_type_ident = Ident::new(
+                        &format!("{}_{}VariantMeta", enum_name, ident),
+                        proc_macro2::Span::call_site()
+                    );
+                    let fields_clone = 
syn::Fields::Named(fields_named.clone());
+                    let sorted_fields = sorted_fields(&fields_clone);
+                    let field_idents: Vec<_> = sorted_fields
+                        .iter()
+                        .map(|f| f.ident.as_ref().unwrap())
+                        .collect();
+
+                    let write_fields: Vec<_> = sorted_fields
+                        .iter()
+                        .zip(field_idents.iter())
+                        .map(|(f, ident)| gen_write_field(f, ident, false))
+                        .collect();
+
+                    quote! {
+                        Self::#ident { #(#field_idents),* } => {
+                            context.writer.write_varuint32((#tag_value << 2) | 
0b10);
+                            // Push named variant meta
+                            let meta_index = 
context.push_meta(std::any::TypeId::of::<#meta_type_ident>())? as u32;
+                            context.writer.write_varuint32(meta_index);
+                            // Write fields same as struct
+                            #(#write_fields)*
+                        }
+                    }
+                }
+            }
+        })
+        .collect()
+}
+
+pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
+    let default_variant_value = data_enum
+        .variants
+        .iter()
+        .position(is_default_value_variant)
+        .unwrap_or(0) as u32;
+
+    let xlang_variant_branches: Vec<TokenStream> =
+        xlang_variant_branches(data_enum, default_variant_value);
+    let rust_variant_branches: Vec<TokenStream> =
+        rust_variant_branches(data_enum, default_variant_value);
+    let rust_compatible_variant_branches: Vec<TokenStream> =
+        rust_compatible_variant_write_branches(data_enum, 
default_variant_value);
 
     quote! {
         if context.is_xlang() {
@@ -171,10 +371,17 @@ pub fn gen_write_data(data_enum: &DataEnum) -> 
TokenStream {
             }
             Ok(())
         } else {
-            match self {
-                #(#rust_variant_branches)*
+            if context.is_compatible() {
+                match self {
+                    #(#rust_compatible_variant_branches)*
+                }
+                Ok(())
+            } else {
+                match self {
+                    #(#rust_variant_branches)*
+                }
+                Ok(())
             }
-            Ok(())
         }
     }
 }
@@ -197,13 +404,11 @@ pub fn gen_read_with_type_info(_: &DataEnum) -> 
TokenStream {
     }
 }
 
-pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
-    let default_variant_value = data_enum
-        .variants
-        .iter()
-        .position(is_default_value_variant)
-        .unwrap_or(0) as u32;
-    let xlang_variant_branches: Vec<TokenStream> = data_enum
+fn xlang_variant_read_branches(
+    data_enum: &DataEnum,
+    default_variant_value: u32,
+) -> Vec<TokenStream> {
+    data_enum
         .variants
         .iter()
         .enumerate()
@@ -249,9 +454,14 @@ pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
                 }
             }
         })
-        .collect();
+        .collect()
+}
 
-    let rust_variant_branches: Vec<TokenStream> = data_enum
+fn rust_variant_read_branches(
+    data_enum: &DataEnum,
+    default_variant_value: u32,
+) -> Vec<TokenStream> {
+    data_enum
         .variants
         .iter()
         .enumerate()
@@ -288,14 +498,8 @@ pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
                     }
                 }
                 Fields::Named(fields_named) => {
-                    let mut sorted_fields: Vec<_> = 
fields_named.named.iter().collect();
-                    sorted_fields.sort_by(|a, b| {
-                        a.ident
-                            .as_ref()
-                            .unwrap()
-                            .to_string()
-                            .cmp(&b.ident.as_ref().unwrap().to_string())
-                    });
+                    let fields_clone = 
syn::Fields::Named(fields_named.clone());
+                    let sorted_fields = sorted_fields(&fields_clone);
 
                     let field_idents: Vec<_> = sorted_fields
                         .iter()
@@ -317,7 +521,196 @@ pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream 
{
                 }
             }
         })
-        .collect();
+        .collect()
+}
+
+fn rust_compatible_variant_read_branches(
+    data_enum: &DataEnum,
+    default_variant_value: u32,
+) -> Vec<TokenStream> {
+    data_enum
+        .variants
+        .iter()
+        .enumerate()
+        .map(|(idx, v)| {
+            let ident = &v.ident;
+            let mut tag_value = idx as u32;
+            if is_skip_enum_variant(v) {
+                tag_value = default_variant_value;
+            }
+
+            match &v.fields {
+                Fields::Unit => {
+                    // Generate default value for this variant
+                    let default_value = quote! { Self::#ident };
+
+                    quote! {
+                        #tag_value => {
+                            // Unit variant should have variant_type == 0b0
+                            if variant_type != 0b0 {
+                                // Variant type mismatch: skip the data and 
use default
+                                use 
fory_core::serializer::skip::skip_enum_variant;
+                                skip_enum_variant(context, variant_type, 
&None)?;
+                                return Ok(#default_value);
+                            }
+                            Ok(Self::#ident)
+                        }
+                    }
+                }
+                Fields::Unnamed(fields_unnamed) => {
+                    // For unnamed enum variants, read using collection format 
(same protocol as tuple)
+                    let field_idents: Vec<_> = 
(0..fields_unnamed.unnamed.len())
+                        .map(|i| Ident::new(&temp_var_name(i), 
proc_macro2::Span::call_site()))
+                        .collect();
+
+                    let field_count = fields_unnamed.unnamed.len();
+
+                    let read_fields: Vec<TokenStream> = fields_unnamed
+                        .unnamed
+                        .iter()
+                        .enumerate()
+                        .map(|(i, field)| {
+                            let field_ident = &field_idents[i];
+                            let field_ty = &field.ty;
+                            quote! {
+                                let #field_ident = if #i < len {
+                                    use fory_core::serializer::Serializer;
+                                    <#field_ty>::fory_read(context, true, 
true)?
+                                } else {
+                                    Default::default()
+                                }
+                            }
+                        })
+                        .collect();
+
+                    // Generate default value for this variant
+                    let default_fields: Vec<TokenStream> = fields_unnamed
+                        .unnamed
+                        .iter()
+                        .map(|_| quote! { Default::default() })
+                        .collect();
+                    let default_value = quote! { Self::#ident( 
#(#default_fields),* ) };
+
+                    quote! {
+                        #tag_value => {
+                            // Unnamed variant should have variant_type == 0b1
+                            if variant_type != 0b1 {
+                                // Variant type mismatch: skip the data and 
use default
+                                use 
fory_core::serializer::skip::skip_enum_variant;
+                                skip_enum_variant(context, variant_type, 
&None)?;
+                                return Ok(#default_value);
+                            }
+                            // Read collection format (same as tuple)
+                            let len = context.reader.read_varuint32()? as 
usize;
+                            let _header = context.reader.read_u8()?;
+
+                            #(#read_fields;)*
+
+                            // Skip any extra elements
+                            use fory_core::serializer::skip::skip_any_value;
+                            for _ in #field_count..len {
+                                skip_any_value(context, true)?;
+                            }
+
+                            Ok(Self::#ident( #(#field_idents),* ))
+                        }
+                    }
+                }
+                Fields::Named(fields_named) => {
+                    use crate::util::sorted_fields;
+
+                    // Sort fields to match the meta type generation
+                    let fields_clone = 
syn::Fields::Named(fields_named.clone());
+                    let sorted_fields_slice = sorted_fields(&fields_clone);
+
+                    // Generate compatible read logic using 
gen_read_compatible_with_construction
+                    let compatible_read_body =
+                        
crate::object::read::gen_read_compatible_with_construction(
+                            &sorted_fields_slice,
+                            Some(ident),
+                        );
+
+                    // Generate default value for this variant
+                    let default_fields: Vec<TokenStream> = fields_named
+                        .named
+                        .iter()
+                        .map(|f| {
+                            let field_ident = f.ident.as_ref().unwrap();
+                            quote! { #field_ident: Default::default() }
+                        })
+                        .collect();
+                    let default_value = quote! { Self::#ident { 
#(#default_fields),* } };
+
+                    quote! {
+                        #tag_value => {
+                            if variant_type != 0b10 {
+                                // Variant type mismatch: peer didn't write 
meta for non-named variant
+                                // Skip the data and use default
+                                use 
fory_core::serializer::skip::skip_enum_variant;
+                                skip_enum_variant(context, variant_type, 
&None)?;
+                                return Ok(#default_value);
+                            }
+                            // Named variant should have variant_type == 0b10
+                            // Read named variant meta (peer wrote this 
because variant_type == 0b10)
+                            let meta_index = context.reader.read_varuint32()? 
as usize;
+                            let type_info = 
context.get_meta(meta_index)?.clone();
+                            // Use gen_read_compatible logic
+                            #compatible_read_body
+                        }
+                    }
+                }
+            }
+        })
+        .collect()
+}
+
+pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
+    let default_variant_value = data_enum
+        .variants
+        .iter()
+        .position(is_default_value_variant)
+        .unwrap_or(0) as u32;
+
+    let xlang_variant_branches: Vec<TokenStream> =
+        xlang_variant_read_branches(data_enum, default_variant_value);
+    let rust_variant_branches: Vec<TokenStream> =
+        rust_variant_read_branches(data_enum, default_variant_value);
+    let rust_compatible_variant_branches: Vec<TokenStream> =
+        rust_compatible_variant_read_branches(data_enum, 
default_variant_value);
+
+    // Get the default variant for compatible mode fallback
+    let default_variant = data_enum
+        .variants
+        .iter()
+        .nth(default_variant_value as usize)
+        .or_else(|| data_enum.variants.first())
+        .unwrap();
+
+    let default_variant_ident = &default_variant.ident;
+    let default_variant_construction = match &default_variant.fields {
+        Fields::Unit => {
+            quote! { Self::#default_variant_ident }
+        }
+        Fields::Unnamed(fields_unnamed) => {
+            let default_fields: Vec<TokenStream> = fields_unnamed
+                .unnamed
+                .iter()
+                .map(|_| quote! { Default::default() })
+                .collect();
+            quote! { Self::#default_variant_ident( #(#default_fields),* ) }
+        }
+        Fields::Named(fields_named) => {
+            let default_fields: Vec<TokenStream> = fields_named
+                .named
+                .iter()
+                .map(|f| {
+                    let field_ident = f.ident.as_ref().unwrap();
+                    quote! { #field_ident: Default::default() }
+                })
+                .collect();
+            quote! { Self::#default_variant_ident { #(#default_fields),* } }
+        }
+    };
 
     quote! {
         if context.is_xlang() {
@@ -327,10 +720,29 @@ pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream 
{
                 _ => return Err(fory_core::error::Error::unknown_enum("unknown 
enum value")),
             }
         } else {
-            let tag = context.reader.read_varuint32()?;
-            match tag {
-                #(#rust_variant_branches)*
-                _ => return Err(fory_core::error::Error::unknown_enum("unknown 
enum value")),
+            if context.is_compatible() {
+                let encoded_tag = context.reader.read_varuint32()?;
+                let tag = encoded_tag >> 2;
+                let variant_type = encoded_tag & 0b11;
+
+                match tag {
+                    #(#rust_compatible_variant_branches)*
+                    _ => {
+                        // Unknown variant in compatible mode: skip the data 
and use default variant
+                        // variant_type: 0b0 = Unit, 0b1 = Unnamed, 0b10 = 
Named
+                        use fory_core::serializer::skip::skip_enum_variant;
+                        // For named variants, we don't have type_info yet, so 
pass None
+                        // skip_enum_variant will read it from the stream
+                        skip_enum_variant(context, variant_type, &None)?;
+                        Ok(#default_variant_construction)
+                    }
+                }
+            } else {
+                let tag = context.reader.read_varuint32()?;
+                match tag {
+                    #(#rust_variant_branches)*
+                    _ => return 
Err(fory_core::error::Error::unknown_enum("unknown enum value")),
+                }
             }
         }
     }
diff --git a/rust/fory-derive/src/object/misc.rs 
b/rust/fory-derive/src/object/misc.rs
index 1e35ebafa..dcbf26472 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -125,8 +125,14 @@ pub fn gen_field_fields_info(fields: &[&Field]) -> 
TokenStream {
             }
         }
     });
+
+    // Get sorted field names for sorting
+    let static_field_names = get_sort_fields_ts(fields);
+
     quote! {
-        let field_infos: Vec<fory_core::meta::FieldInfo> = 
vec![#(#field_infos),*];
+        let mut field_infos: Vec<fory_core::meta::FieldInfo> = 
vec![#(#field_infos),*];
+        let sorted_field_names = #static_field_names;
+        fory_core::meta::sort_fields(&mut field_infos, sorted_field_names)?;
         Ok(field_infos)
     }
 }
diff --git a/rust/fory-derive/src/object/read.rs 
b/rust/fory-derive/src/object/read.rs
index 7160e814b..cc76199d2 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -25,7 +25,7 @@ use super::util::{
     is_primitive_type, is_skip_field, should_skip_type_info_for_field, 
skip_ref_flag, StructField,
 };
 
-fn create_private_field_name(field: &Field) -> Ident {
+pub(crate) fn create_private_field_name(field: &Field) -> Ident {
     format_ident!("_{}", field.ident.as_ref().unwrap())
 }
 
@@ -34,7 +34,7 @@ fn need_declared_by_option(field: &Field) -> bool {
     type_name == "Option" || !is_primitive_type(type_name.as_str())
 }
 
-fn declare_var(fields: &[&Field]) -> Vec<TokenStream> {
+pub(crate) fn declare_var(fields: &[&Field]) -> Vec<TokenStream> {
     fields
         .iter()
         .map(|field| {
@@ -68,7 +68,7 @@ fn declare_var(fields: &[&Field]) -> Vec<TokenStream> {
         .collect()
 }
 
-fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
+pub(crate) fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
     fields
         .iter()
         .map(|field| {
@@ -280,7 +280,7 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
     }
 }
 
-fn gen_read_compatible_match_arm_body(field: &Field, var_name: &Ident) -> 
TokenStream {
+pub(crate) fn gen_read_compatible_match_arm_body(field: &Field, var_name: 
&Ident) -> TokenStream {
     let ty = &field.ty;
     let field_kind = classify_trait_object_field(ty);
     let is_skip_flag = is_skip_field(field);
@@ -527,6 +527,13 @@ pub fn gen_read_with_type_info(struct_ident: &Ident) -> 
TokenStream {
 }
 
 pub fn gen_read_compatible(fields: &[&Field]) -> TokenStream {
+    gen_read_compatible_with_construction(fields, None)
+}
+
+pub(crate) fn gen_read_compatible_with_construction(
+    fields: &[&Field],
+    variant_ident: Option<&Ident>,
+) -> TokenStream {
     let declare_ts: Vec<TokenStream> = declare_var(fields);
     let assign_ts: Vec<TokenStream> = assign_value(fields);
 
@@ -584,6 +591,21 @@ pub fn gen_read_compatible(fields: &[&Field]) -> 
TokenStream {
         }
     };
 
+    // Generate construction based on whether this is a struct or enum variant
+    let construction = if let Some(variant) = variant_ident {
+        quote! {
+            Ok(Self::#variant {
+                #(#assign_ts),*
+            })
+        }
+    } else {
+        quote! {
+            Ok(Self {
+                #(#assign_ts),*
+            })
+        }
+    };
+
     quote! {
         let fields = type_info.get_type_meta().get_field_infos().clone();
         #(#declare_ts)*
@@ -599,9 +621,7 @@ pub fn gen_read_compatible(fields: &[&Field]) -> 
TokenStream {
                     #skip_arm
                 }
             }
-            Ok(Self {
-                #(#assign_ts),*
-            })
+            #construction
         }
     }
 }
diff --git a/rust/fory-derive/src/object/serializer.rs 
b/rust/fory-derive/src/object/serializer.rs
index c2e4c6df2..4c0cece6b 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -50,30 +50,45 @@ pub fn derive_serializer(ast: &syn::DeriveInput, 
debug_enabled: bool) -> TokenSt
     };
 
     // StructSerializer
-    let (actual_type_id_ts, get_sorted_field_names_ts, fields_info_ts, 
read_compatible_ts) =
-        match &ast.data {
-            syn::Data::Struct(s) => {
-                let fields = sorted_fields(&s.fields);
-                (
-                    misc::gen_actual_type_id(),
-                    misc::gen_get_sorted_field_names(&fields),
-                    misc::gen_field_fields_info(&fields),
-                    read::gen_read_compatible(&fields),
-                )
-            }
-            syn::Data::Enum(s) => (
+    let (
+        actual_type_id_ts,
+        get_sorted_field_names_ts,
+        fields_info_ts,
+        variants_fields_info_ts,
+        read_compatible_ts,
+        enum_variant_meta_types,
+    ) = match &ast.data {
+        syn::Data::Struct(s) => {
+            let fields = sorted_fields(&s.fields);
+            (
+                misc::gen_actual_type_id(),
+                misc::gen_get_sorted_field_names(&fields),
+                misc::gen_field_fields_info(&fields),
+                quote! { Ok(Vec::new()) }, // No variants for structs
+                read::gen_read_compatible(&fields),
+                vec![], // No variant meta types for structs
+            )
+        }
+        syn::Data::Enum(s) => {
+            // Generate variant meta types for named variants
+            let variant_meta_types =
+                derive_enum::gen_all_variant_meta_types_with_enum_name(name, 
s);
+            (
                 derive_enum::gen_actual_type_id(),
                 quote! { &[] },
                 derive_enum::gen_field_fields_info(s),
+                derive_enum::gen_variants_fields_info(name, s),
                 quote! {
                     Err(fory_core::Error::not_allowed("`fory_read_compatible` 
should only be invoked at struct type"
                 ))
                 },
-            ),
-            syn::Data::Union(_) => {
-                panic!("Union is not supported")
-            }
-        };
+                variant_meta_types,
+            )
+        }
+        syn::Data::Union(_) => {
+            panic!("Union is not supported")
+        }
+    };
     // Serializer
     let (
         write_ts,
@@ -122,6 +137,9 @@ pub fn derive_serializer(ast: &syn::DeriveInput, 
debug_enabled: bool) -> TokenSt
     let gen = quote! {
         use fory_core::ForyDefault as _;
 
+        // Generate variant meta types for enums (must be at module scope)
+        #(#enum_variant_meta_types)*
+
         #default_impl
 
         impl fory_core::StructSerializer for #name {
@@ -143,6 +161,10 @@ pub fn derive_serializer(ast: &syn::DeriveInput, 
debug_enabled: bool) -> TokenSt
                 #fields_info_ts
             }
 
+            fn fory_variants_fields_info(type_resolver: 
&fory_core::resolver::type_resolver::TypeResolver) -> Result<Vec<(String, 
std::any::TypeId, Vec<fory_core::meta::FieldInfo>)>, fory_core::error::Error> {
+                #variants_fields_info_ts
+            }
+
             #[inline]
             fn fory_read_compatible(context: &mut 
fory_core::resolver::context::ReadContext, type_info: 
std::rc::Rc<fory_core::TypeInfo>) -> Result<Self, fory_core::error::Error> {
                 #read_compatible_ts
diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs
index ee347e6c7..b16fc5fc6 100644
--- a/rust/fory/src/lib.rs
+++ b/rust/fory/src/lib.rs
@@ -654,49 +654,108 @@
 //!
 //! ### 5. Enum Support
 //!
-//! **What it does:** Supports C-style enums (enums without data payloads) 
with efficient
-//! varint encoding.
+//! **What it does:** Comprehensive enum support with three variant types 
(unit, unnamed, named)
+//! and full schema evolution in Compatible mode.
 //!
-//! **Why it matters:** Enums are common for state machines, status codes, and 
type
-//! discriminators. Efficient encoding and schema evolution support are 
essential.
+//! **Why it matters:** Enums are essential for state machines, status codes, 
type discriminators,
+//! and domain modeling. Supporting all variant types with schema evolution 
enables flexible API
+//! evolution without breaking compatibility.
 //!
-//! **Technical approach:** Each variant is assigned an ordinal value (0, 1, 
2, ...)
-//! during serialization. Ordinals are encoded using variable-length integers 
for
-//! space efficiency.
+//! **Technical approach:** Each variant is assigned an ordinal value (0, 1, 
2, ...). In compatible
+//! mode, variants are encoded with both a tag (ordinal) and a type marker (2 
bits: 0b0=Unit,
+//! 0b1=Unnamed, 0b10=Named). Named variants generate meta types for 
field-level evolution.
+//!
+//! **Variant Types:**
+//!
+//! - **Unit**: C-style enums (`Status::Active`)
+//! - **Unnamed**: Tuple-like variants (`Message::Pair(String, i32)`)
+//! - **Named**: Struct-like variants (`Event::Click { x: i32, y: i32 }`)
 //!
 //! **Features:**
 //!
-//! - Efficient varint encoding for ordinals
-//! - Schema evolution support in Compatible mode
-//! - Type-safe variant matching
+//! - Efficient varint encoding for variant ordinals
+//! - Schema evolution support (add/remove variants, add/remove fields)
 //! - Default variant support with `#[default]`
+//! - Automatic type mismatch handling
 //!
 //! ```rust
 //! use fory::Fory;
 //! use fory::Error;
 //! use fory::ForyObject;
 //!
-//! #[derive(ForyObject, Debug, PartialEq, Default)]
-//! enum Status {
+//! #[derive(Default, ForyObject, Debug, PartialEq)]
+//! enum Value {
 //!     #[default]
-//!     Pending,
-//!     Active,
-//!     Inactive,
-//!     Deleted,
+//!     Null,
+//!     Bool(bool),
+//!     Number(f64),
+//!     Text(String),
+//!     Object { name: String, value: i32 },
 //! }
 //!
 //! # fn main() -> Result<(), Error> {
 //! let mut fory = Fory::default();
-//! fory.register::<Status>(1);
+//! fory.register::<Value>(1)?;
+//!
+//! let value = Value::Object { name: "score".to_string(), value: 100 };
+//! let bytes = fory.serialize(&value)?;
+//! let decoded: Value = fory.deserialize(&bytes)?;
+//! assert_eq!(value, decoded);
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! **Schema Evolution:**
+//!
+//! Compatible mode enables robust schema evolution with variant type encoding:
+//!
+//! ```rust
+//! use fory::Fory;
+//! use fory::Error;
+//! use fory::ForyObject;
+//!
+//! // Old version with 2 fields
+//! #[derive(ForyObject, Debug)]
+//! enum OldEvent {
+//!     Click { x: i32, y: i32 },
+//! }
+//!
+//! // New version with 3 fields - added timestamp
+//! #[derive(ForyObject, Debug)]
+//! enum NewEvent {
+//!     Click { x: i32, y: i32, timestamp: u64 },
+//! }
 //!
-//! let status = Status::Active;
-//! let bytes = fory.serialize(&status)?;
-//! let decoded: Status = fory.deserialize(&bytes)?;
-//! assert_eq!(status, decoded);
+//! # fn main() -> Result<(), Error> {
+//! let mut fory_old = Fory::default().compatible(true);
+//! fory_old.register::<OldEvent>(5)?;
+//!
+//! let mut fory_new = Fory::default().compatible(true);
+//! fory_new.register::<NewEvent>(5)?;
+//!
+//! // Serialize with old schema (2 fields)
+//! let old_bytes = fory_old.serialize(&OldEvent::Click { x: 100, y: 200 })?;
+//!
+//! // Deserialize with new schema (3 fields) - timestamp gets default value 
(0)
+//! let new_event: NewEvent = fory_new.deserialize(&old_bytes)?;
+//! match new_event {
+//!     NewEvent::Click { x, y, timestamp } => {
+//!         assert_eq!(x, 100);
+//!         assert_eq!(y, 200);
+//!         assert_eq!(timestamp, 0); // Default value for missing field
+//!     }
+//! }
 //! # Ok(())
 //! # }
 //! ```
 //!
+//! **Evolution capabilities:**
+//!
+//! - Unknown variants fall back to default variant
+//! - Named variant fields: add/remove fields (missing fields use defaults)
+//! - Unnamed variant elements: add/remove elements (extras skipped, missing 
use defaults)
+//! - Variant type mismatches automatically use default value of current 
variant
+//!
 //! ### 6. Tuple Support
 //!
 //! **What it does:** Supports tuples up to 22 elements with automatic 
heterogeneous type
diff --git a/rust/tests/tests/test_enum_compatible.rs 
b/rust/tests/tests/test_enum_compatible.rs
new file mode 100644
index 000000000..8d5c0f383
--- /dev/null
+++ b/rust/tests/tests/test_enum_compatible.rs
@@ -0,0 +1,735 @@
+// 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.
+
+// RUSTFLAGS="-Awarnings" cargo expand -p tests --test test_enum
+
+use fory_core::Fory;
+use fory_derive::ForyObject;
+
+/// Test schema evolution for unnamed enum variants in compatible mode
+#[test]
+fn test_unnamed_enum_variant_compatible() {
+    // Original enum with 2 fields in variant
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum EventV1 {
+        #[fory(default)]
+        Unknown,
+        Message(i32, String),
+    }
+
+    // Evolved enum with 3 fields in variant (added f64)
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum EventV2 {
+        #[fory(default)]
+        Unknown,
+        Message(i32, String, f64),
+    }
+
+    // Test 1: Serialize v1 (2 fields), deserialize as v2 (3 fields)
+    let mut fory_v1 = Fory::default().xlang(false).compatible(true);
+    fory_v1.register::<EventV1>(2000).unwrap();
+
+    let mut fory_v2 = Fory::default().xlang(false).compatible(true);
+    fory_v2.register::<EventV2>(2000).unwrap();
+
+    let event_v1 = EventV1::Message(42, "hello".to_string());
+    let bin = fory_v1.serialize(&event_v1).unwrap();
+    let event_v2: EventV2 = fory_v2.deserialize(&bin).expect("deserialize v1 
to v2");
+    match event_v2 {
+        EventV2::Message(a, b, c) => {
+            assert_eq!(a, 42);
+            assert_eq!(b, "hello");
+            assert_eq!(c, 0.0); // Default value for missing field
+        }
+        _ => panic!("Expected Message variant"),
+    }
+
+    // Test 2: Serialize v2 (3 fields), deserialize as v1 (2 fields)
+    let event_v2 = EventV2::Message(100, "world".to_string(), 
std::f64::consts::PI);
+    let bin = fory_v2.serialize(&event_v2).unwrap();
+    let event_v1: EventV1 = fory_v1.deserialize(&bin).expect("deserialize v2 
to v1");
+    match event_v1 {
+        EventV1::Message(a, b) => {
+            assert_eq!(a, 100);
+            assert_eq!(b, "world");
+            // Extra field (3.14) is skipped during deserialization
+        }
+        _ => panic!("Expected Message variant"),
+    }
+
+    // Test 3: Unknown variant falls back to default
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum EventV3 {
+        #[fory(default)]
+        Unknown,
+        Message(i32, String),
+        NewVariant(bool), // This variant doesn't exist in EventV1
+    }
+
+    let mut fory_v3 = Fory::default().xlang(false).compatible(true);
+    fory_v3.register::<EventV3>(2000).unwrap();
+
+    let event_v3 = EventV3::NewVariant(true);
+    let bin = fory_v3.serialize(&event_v3).unwrap();
+    let event_v1: EventV1 = fory_v1
+        .deserialize(&bin)
+        .expect("deserialize unknown variant");
+    assert_eq!(event_v1, EventV1::Unknown); // Falls back to default variant
+}
+
+/// Test schema evolution for named enum variants in compatible mode
+#[test]
+fn test_named_enum_variant_compatible() {
+    // Original enum with 2 fields
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum CommandV1 {
+        #[fory(default)]
+        Noop,
+        Execute {
+            args: i32,
+            name: String,
+        },
+    }
+
+    // Evolved enum with 3 fields - added 'env' field
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum CommandV2 {
+        #[fory(default)]
+        Noop,
+        Execute {
+            args: i32,
+            env: String,
+            name: String,
+        },
+    }
+
+    let mut fory_v1 = Fory::default().xlang(false).compatible(true);
+    fory_v1.register::<CommandV1>(3000).unwrap();
+
+    let mut fory_v2 = Fory::default().xlang(false).compatible(true);
+    fory_v2.register::<CommandV2>(3000).unwrap();
+
+    // Test 1: Serialize v1, deserialize as v2 (new field gets default value)
+    let cmd_v1 = CommandV1::Execute {
+        args: 42,
+        name: "run".to_string(),
+    };
+    let bin = fory_v1.serialize(&cmd_v1).unwrap();
+    let cmd_v2: CommandV2 = fory_v2.deserialize(&bin).expect("deserialize v1 
to v2");
+    match cmd_v2 {
+        CommandV2::Execute { args, env, name } => {
+            assert_eq!(args, 42);
+            assert_eq!(name, "run");
+            assert_eq!(env, ""); // Default value for missing field
+        }
+        _ => panic!("Expected Execute variant"),
+    }
+
+    // Test 2: Serialize v2, deserialize as v1 (extra field is skipped)
+    let cmd_v2 = CommandV2::Execute {
+        args: 100,
+        env: "prod".to_string(),
+        name: "test".to_string(),
+    };
+    let bin = fory_v2.serialize(&cmd_v2).unwrap();
+    let cmd_v1: CommandV1 = fory_v1.deserialize(&bin).expect("deserialize v2 
to v1");
+    match cmd_v1 {
+        CommandV1::Execute { args, name } => {
+            assert_eq!(args, 100);
+            assert_eq!(name, "test");
+            // 'env' field is skipped
+        }
+        _ => panic!("Expected Execute variant"),
+    }
+}
+
+/// Test 1: Named enum variant with field changes (add/change/reduce simple 
and complex fields)
+#[test]
+fn test_named_enum_field_evolution() {
+    // Version 1: Original schema with basic fields
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum ConfigV1 {
+        #[fory(default)]
+        Empty,
+        Settings {
+            timeout: i32,
+            retry: bool,
+        },
+    }
+
+    // Version 2: Add simple field and complex field (Vec)
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum ConfigV2 {
+        #[fory(default)]
+        Empty,
+        Settings {
+            timeout: i32,
+            retry: bool,
+            max_connections: u32,       // New simple field
+            allowed_hosts: Vec<String>, // New complex field
+        },
+    }
+
+    // Version 3: Change field type and remove field
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum ConfigV3 {
+        #[fory(default)]
+        Empty,
+        Settings {
+            timeout: i32, // Keep same type for compatibility
+            // retry field removed
+            max_connections: u32,
+            allowed_hosts: Vec<String>,
+            metadata: Option<String>, // New optional field
+        },
+    }
+
+    let mut fory_v1 = Fory::default().xlang(false).compatible(true);
+    fory_v1.register::<ConfigV1>(4000).unwrap();
+
+    let mut fory_v2 = Fory::default().xlang(false).compatible(true);
+    fory_v2.register::<ConfigV2>(4000).unwrap();
+
+    let mut fory_v3 = Fory::default().xlang(false).compatible(true);
+    fory_v3.register::<ConfigV3>(4000).unwrap();
+
+    // Test V1 -> V2: New fields get default values
+    let config_v1 = ConfigV1::Settings {
+        timeout: 30,
+        retry: true,
+    };
+    let bin = fory_v1.serialize(&config_v1).unwrap();
+    let config_v2: ConfigV2 = fory_v2.deserialize(&bin).expect("v1 to v2");
+    match config_v2 {
+        ConfigV2::Settings {
+            timeout,
+            retry,
+            max_connections,
+            allowed_hosts,
+        } => {
+            assert_eq!(timeout, 30);
+            assert!(retry);
+            assert_eq!(max_connections, 0); // Default value
+            assert_eq!(allowed_hosts, Vec::<String>::new()); // Empty vector
+        }
+        _ => panic!("Expected Settings variant"),
+    }
+
+    // Test V2 -> V1: Extra fields are skipped
+    let config_v2 = ConfigV2::Settings {
+        timeout: 60,
+        retry: false,
+        max_connections: 100,
+        allowed_hosts: vec!["localhost".to_string(), 
"example.com".to_string()],
+    };
+    let bin = fory_v2.serialize(&config_v2).unwrap();
+    let config_v1: ConfigV1 = fory_v1.deserialize(&bin).expect("v2 to v1");
+    match config_v1 {
+        ConfigV1::Settings { timeout, retry } => {
+            assert_eq!(timeout, 60);
+            assert!(!retry);
+            // max_connections and allowed_hosts are skipped
+        }
+        _ => panic!("Expected Settings variant"),
+    }
+
+    // Test V2 -> V3: Field removal (retry field is removed)
+    let config_v2_2 = ConfigV2::Settings {
+        timeout: 45,
+        retry: true,
+        max_connections: 50,
+        allowed_hosts: vec!["api.example.com".to_string()],
+    };
+    let bin = fory_v2.serialize(&config_v2_2).unwrap();
+    let config_v3: ConfigV3 = fory_v3.deserialize(&bin).expect("v2 to v3");
+    match config_v3 {
+        ConfigV3::Settings {
+            timeout,
+            max_connections,
+            allowed_hosts,
+            metadata,
+        } => {
+            assert_eq!(timeout, 45); // Same type preserved
+            assert_eq!(max_connections, 50);
+            assert_eq!(allowed_hosts, vec!["api.example.com".to_string()]);
+            assert_eq!(metadata, None); // New optional field gets None
+        }
+        _ => panic!("Expected Settings variant"),
+    }
+}
+
+/// Test 2: Enum add/remove named variant with complex fields
+#[test]
+fn test_named_enum_variant_add_remove() {
+    // Version 1: Two variants
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum TaskV1 {
+        #[fory(default)]
+        Idle,
+        Running {
+            task_id: u64,
+            progress: f32,
+        },
+    }
+
+    // Version 2: Add new named variant with complex fields
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum TaskV2 {
+        #[fory(default)]
+        Idle,
+        Running {
+            task_id: u64,
+            progress: f32,
+        },
+        Completed {
+            task_id: u64,
+            result: Vec<String>,
+            metadata: Option<Vec<i32>>,
+        },
+    }
+
+    // Version 3: Remove Running, add Failed variant
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum TaskV3 {
+        #[fory(default)]
+        Idle,
+        // Running removed
+        Completed {
+            task_id: u64,
+            result: Vec<String>,
+            metadata: Option<Vec<i32>>,
+        },
+        Failed {
+            error_code: i32,
+            error_message: String,
+        },
+    }
+
+    let mut fory_v1 = Fory::default().xlang(false).compatible(true);
+    fory_v1.register::<TaskV1>(5000).unwrap();
+
+    let mut fory_v2 = Fory::default().xlang(false).compatible(true);
+    fory_v2.register::<TaskV2>(5000).unwrap();
+
+    let mut fory_v3 = Fory::default().xlang(false).compatible(true);
+    fory_v3.register::<TaskV3>(5000).unwrap();
+
+    // Test V2 (new variant Completed) -> V1: Unknown variant falls back to 
default
+    let task_v2 = TaskV2::Completed {
+        task_id: 123,
+        result: vec!["success".to_string(), "data".to_string()],
+        metadata: Some(vec![1, 2, 3]),
+    };
+    let bin = fory_v2.serialize(&task_v2).unwrap();
+    let task_v1: TaskV1 = fory_v1.deserialize(&bin).expect("v2 to v1");
+    assert_eq!(task_v1, TaskV1::Idle); // Falls back to default
+
+    // Test V1 (Running) -> V2: Existing variant deserializes correctly
+    let task_v1 = TaskV1::Running {
+        task_id: 456,
+        progress: 0.75,
+    };
+    let bin = fory_v1.serialize(&task_v1).unwrap();
+    let task_v2: TaskV2 = fory_v2.deserialize(&bin).expect("v1 to v2");
+    match task_v2 {
+        TaskV2::Running { task_id, progress } => {
+            assert_eq!(task_id, 456);
+            assert_eq!(progress, 0.75);
+        }
+        _ => panic!("Expected Running variant"),
+    }
+
+    // Test V2 (Running) -> V3: Running (index 1) maps to Completed (index 1)
+    // In compatible mode, variant indices are preserved, so they match by 
position
+    let task_v2_running = TaskV2::Running {
+        task_id: 789,
+        progress: 0.5,
+    };
+    let bin = fory_v2.serialize(&task_v2_running).unwrap();
+    let task_v3: TaskV3 = fory_v3.deserialize(&bin).expect("v2 running to v3");
+    // V2's Running (index 1) maps to V3's Completed (index 1) by variant index
+    match task_v3 {
+        TaskV3::Completed {
+            task_id,
+            result,
+            metadata,
+        } => {
+            // Fields are read based on V3's Completed schema, but data was 
from V2's Running
+            // This demonstrates schema mismatch - fields get default values 
or partial data
+            assert_eq!(task_id, 789); // task_id exists in both, so preserved
+            assert_eq!(result, Vec::<String>::new()); // Not in source, gets 
default
+            assert_eq!(metadata, None); // Not in source, gets default
+        }
+        _ => panic!("Expected Completed variant (mapped from Running by 
index)"),
+    }
+
+    // Test V3 (Failed) -> V2: V3's Failed (index 2) maps to V2's Completed 
(index 2)
+    let task_v3 = TaskV3::Failed {
+        error_code: 500,
+        error_message: "Internal error".to_string(),
+    };
+    let bin = fory_v3.serialize(&task_v3).unwrap();
+    let task_v2: TaskV2 = fory_v2.deserialize(&bin).expect("v3 failed to v2");
+    // V3's Failed (index 2) maps to V2's Completed (index 2) by variant index
+    match task_v2 {
+        TaskV2::Completed {
+            task_id,
+            result,
+            metadata,
+        } => {
+            // Fields get default values since schemas don't match
+            assert_eq!(task_id, 0); // Default value
+            assert_eq!(result, Vec::<String>::new()); // Default value
+            assert_eq!(metadata, None); // Default value
+        }
+        _ => panic!("Expected Completed variant (mapped from Failed by 
index)"),
+    }
+
+    // Test V3 (Completed) -> V2: V3's Completed (index 1) maps to V2's 
Running (index 1)
+    let task_v3_completed = TaskV3::Completed {
+        task_id: 999,
+        result: vec!["done".to_string()],
+        metadata: None,
+    };
+    let bin = fory_v3.serialize(&task_v3_completed).unwrap();
+    let task_v2: TaskV2 = fory_v2.deserialize(&bin).expect("v3 completed to 
v2");
+    // V3's Completed (index 1) maps to V2's Running (index 1) by variant index
+    match task_v2 {
+        TaskV2::Running { task_id, progress } => {
+            // Fields are read based on V2's Running schema
+            assert_eq!(task_id, 999); // task_id exists in both, preserved
+            assert_eq!(progress, 0.0); // Default value (not in V3's Completed)
+        }
+        _ => panic!("Expected Running variant (mapped from Completed by 
index)"),
+    }
+}
+
+/// Test 3: Change enum variant type between unit/unnamed/named
+#[test]
+fn test_enum_variant_type_change() {
+    // Version 1: Different variant types
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum StatusV1 {
+        #[fory(default)]
+        Unknown,
+        Active,          // Unit variant
+        Processing(i32), // Unnamed variant
+        Finished {
+            code: i32,
+        }, // Named variant
+    }
+
+    // Version 2: Change variant types
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum StatusV2 {
+        #[fory(default)]
+        Unknown,
+        Active {
+            timestamp: i64,
+        }, // Unit -> Named
+        Processing {
+            value: i32,
+            name: String,
+        }, // Unnamed -> Named
+        Finished(i32), // Named -> Unnamed
+    }
+
+    // Version 3: More type changes
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum StatusV3 {
+        #[fory(default)]
+        Unknown,
+        Active,                  // Named -> Unit
+        Processing(i32, String), // Named -> Unnamed (2 fields)
+        Finished,                // Unnamed -> Unit
+    }
+
+    let mut fory_v1 = Fory::default().xlang(false).compatible(true);
+    fory_v1.register::<StatusV1>(6000).unwrap();
+
+    let mut fory_v2 = Fory::default().xlang(false).compatible(true);
+    fory_v2.register::<StatusV2>(6000).unwrap();
+
+    let mut fory_v3 = Fory::default().xlang(false).compatible(true);
+    fory_v3.register::<StatusV3>(6000).unwrap();
+
+    // Test V1 Unit -> V2 Named: Type mismatch, uses default value
+    let status_v1_active = StatusV1::Active;
+    let bin = fory_v1.serialize(&status_v1_active).unwrap();
+    let status_v2: StatusV2 = fory_v2.deserialize(&bin).expect("v1 unit to v2 
named");
+    match status_v2 {
+        StatusV2::Active { timestamp } => {
+            assert_eq!(timestamp, 0); // Default value for the named variant
+        }
+        _ => panic!("Expected Active variant with default fields"),
+    }
+
+    // Test V1 Unnamed -> V2 Named: Type mismatch, uses default value
+    let status_v1_processing = StatusV1::Processing(42);
+    let bin = fory_v1.serialize(&status_v1_processing).unwrap();
+    let status_v2: StatusV2 = fory_v2.deserialize(&bin).expect("v1 unnamed to 
v2 named");
+    match status_v2 {
+        StatusV2::Processing { value, name } => {
+            assert_eq!(value, 0); // Default value
+            assert_eq!(name, ""); // Default value
+        }
+        _ => panic!("Expected Processing variant with default fields"),
+    }
+
+    // Test V1 Named -> V2 Unnamed: Type mismatch, uses default value
+    let status_v1_finished = StatusV1::Finished { code: 200 };
+    let bin = fory_v1.serialize(&status_v1_finished).unwrap();
+    let status_v2: StatusV2 = fory_v2.deserialize(&bin).expect("v1 named to v2 
unnamed");
+    match status_v2 {
+        StatusV2::Finished(code) => {
+            assert_eq!(code, 0); // Default value for unnamed variant
+        }
+        _ => panic!("Expected Finished variant with default value"),
+    }
+
+    // Test V2 Named -> V3 Unit: Type mismatch, uses default unit variant
+    let status_v2_active = StatusV2::Active { timestamp: 123456 };
+    let bin = fory_v2.serialize(&status_v2_active).unwrap();
+    let status_v3: StatusV3 = fory_v3.deserialize(&bin).expect("v2 named to v3 
unit");
+    assert_eq!(status_v3, StatusV3::Active); // Unit variant
+
+    // Test V2 Named -> V3 Unnamed (2 fields): Type mismatch, uses default 
values
+    let status_v2_processing = StatusV2::Processing {
+        value: 99,
+        name: "test".to_string(),
+    };
+    let bin = fory_v2.serialize(&status_v2_processing).unwrap();
+    let status_v3: StatusV3 = fory_v3.deserialize(&bin).expect("v2 named to v3 
unnamed");
+    match status_v3 {
+        StatusV3::Processing(value, name) => {
+            assert_eq!(value, 0); // Default value
+            assert_eq!(name, ""); // Default value
+        }
+        _ => panic!("Expected Processing variant with default values"),
+    }
+
+    // Test V2 Unnamed -> V3 Unit: Type mismatch, uses default unit variant
+    let status_v2_finished = StatusV2::Finished(404);
+    let bin = fory_v2.serialize(&status_v2_finished).unwrap();
+    let status_v3: StatusV3 = fory_v3.deserialize(&bin).expect("v2 unnamed to 
v3 unit");
+    assert_eq!(status_v3, StatusV3::Finished); // Unit variant
+}
+
+/// Test 4: Struct containing enum with schema evolution
+#[test]
+fn test_struct_with_enum_field_evolution() {
+    // Version 1: Struct with enum field
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum StateV1 {
+        #[fory(default)]
+        Init,
+        Ready {
+            id: u32,
+        },
+    }
+
+    #[derive(ForyObject, Debug, PartialEq)]
+    struct MessageV1 {
+        timestamp: i64,
+        state: StateV1,
+        payload: String,
+    }
+
+    // Version 2: Enum evolved with new fields and variant
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum StateV2 {
+        #[fory(default)]
+        Init,
+        Ready {
+            id: u32,
+            version: String, // New field
+        },
+        Error {
+            code: i32,
+            message: String,
+        }, // New variant
+    }
+
+    #[derive(ForyObject, Debug, PartialEq)]
+    struct MessageV2 {
+        timestamp: i64,
+        state: StateV2,
+        payload: String,
+        priority: i32, // New field in struct
+    }
+
+    // Version 3: Enum variant type changed
+    #[derive(ForyObject, Debug, PartialEq)]
+    enum StateV3 {
+        #[fory(default)]
+        Init,
+        Ready(u32, String), // Changed from named to unnamed
+        Error {
+            code: i32,
+            message: String,
+        },
+    }
+
+    #[derive(ForyObject, Debug, PartialEq)]
+    struct MessageV3 {
+        timestamp: i64,
+        state: StateV3,
+        payload: String,
+        priority: i32,
+        sender: Option<String>, // New optional field
+    }
+
+    let mut fory_v1 = Fory::default().xlang(false).compatible(true);
+    fory_v1.register::<StateV1>(7001).unwrap();
+    fory_v1.register::<MessageV1>(7000).unwrap();
+
+    let mut fory_v2 = Fory::default().xlang(false).compatible(true);
+    fory_v2.register::<StateV2>(7001).unwrap();
+    fory_v2.register::<MessageV2>(7000).unwrap();
+
+    let mut fory_v3 = Fory::default().xlang(false).compatible(true);
+    fory_v3.register::<StateV3>(7001).unwrap();
+    fory_v3.register::<MessageV3>(7000).unwrap();
+
+    // Test V1 -> V2: Enum field evolution
+    let msg_v1 = MessageV1 {
+        timestamp: 1000,
+        state: StateV1::Ready { id: 42 },
+        payload: "hello".to_string(),
+    };
+    let bin = fory_v1.serialize(&msg_v1).unwrap();
+    let msg_v2: MessageV2 = fory_v2.deserialize(&bin).expect("v1 to v2");
+    assert_eq!(msg_v2.timestamp, 1000);
+    match msg_v2.state {
+        StateV2::Ready { id, version } => {
+            assert_eq!(id, 42);
+            assert_eq!(version, ""); // Default value for new enum field
+        }
+        _ => panic!("Expected Ready variant"),
+    }
+    assert_eq!(msg_v2.payload, "hello");
+    assert_eq!(msg_v2.priority, 0); // Default value for new struct field
+
+    // Test V2 -> V1: Extra fields skipped
+    let msg_v2 = MessageV2 {
+        timestamp: 2000,
+        state: StateV2::Ready {
+            id: 100,
+            version: "v2.0".to_string(),
+        },
+        payload: "world".to_string(),
+        priority: 5,
+    };
+    let bin = fory_v2.serialize(&msg_v2).unwrap();
+    let msg_v1: MessageV1 = fory_v1.deserialize(&bin).expect("v2 to v1");
+    assert_eq!(msg_v1.timestamp, 2000);
+    match msg_v1.state {
+        StateV1::Ready { id } => {
+            assert_eq!(id, 100);
+            // version field skipped
+        }
+        _ => panic!("Expected Ready variant"),
+    }
+    assert_eq!(msg_v1.payload, "world");
+    // priority field skipped
+
+    // Test V2 (new variant) -> V1: Unknown enum variant falls back to default
+    let msg_v2_error = MessageV2 {
+        timestamp: 3000,
+        state: StateV2::Error {
+            code: 500,
+            message: "error".to_string(),
+        },
+        payload: "fail".to_string(),
+        priority: 10,
+    };
+    let bin = fory_v2.serialize(&msg_v2_error).unwrap();
+    let msg_v1: MessageV1 = fory_v1.deserialize(&bin).expect("v2 error to v1");
+    assert_eq!(msg_v1.timestamp, 3000);
+    assert_eq!(msg_v1.state, StateV1::Init); // Falls back to default
+    assert_eq!(msg_v1.payload, "fail");
+
+    // Test V2 -> V3: Enum variant type change (named to unnamed)
+    let msg_v2_ready = MessageV2 {
+        timestamp: 4000,
+        state: StateV2::Ready {
+            id: 200,
+            version: "v2.5".to_string(),
+        },
+        payload: "test".to_string(),
+        priority: 3,
+    };
+    let bin = fory_v2.serialize(&msg_v2_ready).unwrap();
+    let msg_v3: MessageV3 = fory_v3.deserialize(&bin).expect("v2 to v3");
+    assert_eq!(msg_v3.timestamp, 4000);
+    match msg_v3.state {
+        StateV3::Ready(id, version) => {
+            assert_eq!(id, 0); // Default value due to type mismatch
+            assert_eq!(version, ""); // Default value due to type mismatch
+        }
+        _ => panic!("Expected Ready variant with default values"),
+    }
+    assert_eq!(msg_v3.payload, "test");
+    assert_eq!(msg_v3.priority, 3);
+    assert_eq!(msg_v3.sender, None); // New optional field
+
+    // Test V3 -> V2: Enum variant type change (unnamed to named)
+    let msg_v3 = MessageV3 {
+        timestamp: 5000,
+        state: StateV3::Ready(300, "v3.0".to_string()),
+        payload: "data".to_string(),
+        priority: 7,
+        sender: Some("system".to_string()),
+    };
+    let bin = fory_v3.serialize(&msg_v3).unwrap();
+    let msg_v2: MessageV2 = fory_v2.deserialize(&bin).expect("v3 to v2");
+    assert_eq!(msg_v2.timestamp, 5000);
+    match msg_v2.state {
+        StateV2::Ready { id, version } => {
+            assert_eq!(id, 0); // Default value due to type mismatch
+            assert_eq!(version, ""); // Default value due to type mismatch
+        }
+        _ => panic!("Expected Ready variant with default values"),
+    }
+    assert_eq!(msg_v2.payload, "data");
+    assert_eq!(msg_v2.priority, 7);
+    // sender field skipped
+
+    // Test V3 (Error variant) -> V2 (Error variant): Same variant works
+    let msg_v3_error = MessageV3 {
+        timestamp: 6000,
+        state: StateV3::Error {
+            code: 404,
+            message: "not found".to_string(),
+        },
+        payload: "error".to_string(),
+        priority: 1,
+        sender: None,
+    };
+    let bin = fory_v3.serialize(&msg_v3_error).unwrap();
+    let msg_v2: MessageV2 = fory_v2.deserialize(&bin).expect("v3 error to v2");
+    assert_eq!(msg_v2.timestamp, 6000);
+    match msg_v2.state {
+        StateV2::Error { code, message } => {
+            assert_eq!(code, 404);
+            assert_eq!(message, "not found");
+        }
+        _ => panic!("Expected Error variant"),
+    }
+    assert_eq!(msg_v2.payload, "error");
+    assert_eq!(msg_v2.priority, 1);
+}


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

Reply via email to