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 3d3b0a735 feat(rust): add unit type and PhantomData serializer support 
(#3081)
3d3b0a735 is described below

commit 3d3b0a73549ca0564a82bd40e21f2ad5f0624d4e
Author: Damon Zhao <[email protected]>
AuthorDate: Wed Dec 24 14:53:23 2025 +0800

    feat(rust): add unit type and PhantomData serializer support (#3081)
    
    ## Why?
    
    To support serialization of `()` and `PhantomData` types in Rust.
    
    ## What does this PR do?
    
    This PR adds support for `()` and `PhantomData` types:
    
    
    ## Related issues
    
    None
    
    ## Does this PR introduce any user-facing change?
    
    - [x] Does this PR introduce any public API change?
    - Yes, adds new public types support: `()` and `PhantomData` can now be
    serialized/deserialized
    - [ ] Does this PR introduce any binary protocol compatibility change?
      - No
    
    ## Benchmark
---
 rust/fory-core/src/serializer/marker.rs |  74 ++++++++++++++++++++++
 rust/fory-core/src/serializer/mod.rs    |   1 +
 rust/fory-core/src/serializer/tuple.rs  |  45 ++++++++++++++
 rust/tests/tests/test_marker.rs         | 107 ++++++++++++++++++++++++++++++++
 rust/tests/tests/test_tuple.rs          |  43 +++++++++++++
 5 files changed, 270 insertions(+)

diff --git a/rust/fory-core/src/serializer/marker.rs 
b/rust/fory-core/src/serializer/marker.rs
new file mode 100644
index 000000000..49cc198cb
--- /dev/null
+++ b/rust/fory-core/src/serializer/marker.rs
@@ -0,0 +1,74 @@
+// 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.
+
+//! Serializer implementations for marker types like `PhantomData<T>`.
+
+use crate::error::Error;
+use crate::resolver::context::{ReadContext, WriteContext};
+use crate::resolver::type_resolver::TypeResolver;
+use crate::serializer::{ForyDefault, Serializer};
+use crate::types::TypeId;
+use std::marker::PhantomData;
+
+impl<T: 'static> Serializer for PhantomData<T> {
+    #[inline(always)]
+    fn fory_write_data(&self, _context: &mut WriteContext) -> Result<(), 
Error> {
+        // PhantomData has no data to write
+        Ok(())
+    }
+
+    #[inline(always)]
+    fn fory_read_data(_context: &mut ReadContext) -> Result<Self, Error> {
+        // PhantomData has no data to read
+        Ok(PhantomData)
+    }
+
+    #[inline(always)]
+    fn fory_reserved_space() -> usize {
+        0
+    }
+
+    #[inline(always)]
+    fn fory_get_type_id(_: &TypeResolver) -> Result<u32, Error> {
+        // Use UNKNOWN to skip type registry lookup - PhantomData<T> has no 
runtime data
+        Ok(TypeId::UNKNOWN as u32)
+    }
+
+    #[inline(always)]
+    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<u32, Error> {
+        // Use UNKNOWN to skip type registry lookup - PhantomData<T> has no 
runtime data
+        Ok(TypeId::UNKNOWN as u32)
+    }
+
+    #[inline(always)]
+    fn fory_static_type_id() -> TypeId {
+        // Use UNKNOWN to skip type registry lookup - PhantomData<T> has no 
runtime data
+        TypeId::UNKNOWN
+    }
+
+    #[inline(always)]
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl<T: 'static> ForyDefault for PhantomData<T> {
+    #[inline(always)]
+    fn fory_default() -> Self {
+        PhantomData
+    }
+}
diff --git a/rust/fory-core/src/serializer/mod.rs 
b/rust/fory-core/src/serializer/mod.rs
index 66686b331..baf70905c 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -26,6 +26,7 @@ pub mod enum_;
 mod heap;
 mod list;
 pub mod map;
+mod marker;
 mod mutex;
 mod number;
 mod option;
diff --git a/rust/fory-core/src/serializer/tuple.rs 
b/rust/fory-core/src/serializer/tuple.rs
index 066e77f5f..50b631003 100644
--- a/rust/fory-core/src/serializer/tuple.rs
+++ b/rust/fory-core/src/serializer/tuple.rs
@@ -24,6 +24,51 @@ use crate::serializer::{ForyDefault, Serializer};
 use crate::types::TypeId;
 use std::mem;
 
+// Unit type () implementation
+impl Serializer for () {
+    #[inline(always)]
+    fn fory_write_data(&self, _context: &mut WriteContext) -> Result<(), 
Error> {
+        // Unit type has no data to write
+        Ok(())
+    }
+
+    #[inline(always)]
+    fn fory_read_data(_context: &mut ReadContext) -> Result<Self, Error> {
+        // Unit type has no data to read
+        Ok(())
+    }
+
+    #[inline(always)]
+    fn fory_reserved_space() -> usize {
+        0
+    }
+
+    #[inline(always)]
+    fn fory_get_type_id(_: &TypeResolver) -> Result<u32, Error> {
+        Ok(TypeId::STRUCT as u32)
+    }
+
+    #[inline(always)]
+    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<u32, Error> {
+        Ok(TypeId::STRUCT as u32)
+    }
+
+    #[inline(always)]
+    fn fory_static_type_id() -> TypeId {
+        TypeId::STRUCT
+    }
+
+    #[inline(always)]
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl ForyDefault for () {
+    #[inline(always)]
+    fn fory_default() -> Self {}
+}
+
 /// Helper function to write a tuple element based on its type characteristics.
 /// This handles the different serialization strategies for various element 
types.
 #[inline(always)]
diff --git a/rust/tests/tests/test_marker.rs b/rust/tests/tests/test_marker.rs
new file mode 100644
index 000000000..90b41afa0
--- /dev/null
+++ b/rust/tests/tests/test_marker.rs
@@ -0,0 +1,107 @@
+// 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.
+
+//! Tests for marker types like `PhantomData<T>`.
+//!
+//! `PhantomData<T>` is a zero-sized marker type used for type-level 
information
+//! without any runtime data. These tests verify that structs containing
+//! `PhantomData<T>` can be serialized correctly.
+
+use fory_core::fory::Fory;
+use fory_derive::ForyObject;
+use std::marker::PhantomData;
+
+/// Test struct containing PhantomData with concrete type
+#[derive(Debug, PartialEq, ForyObject)]
+struct StructWithPhantom {
+    name: String,
+    _marker: PhantomData<i32>,
+    count: i32,
+}
+
+#[test]
+fn test_struct_with_phantom_data() {
+    let mut fory = Fory::default();
+    fory.register::<StructWithPhantom>(100).unwrap();
+
+    let value = StructWithPhantom {
+        name: "test".to_string(),
+        _marker: PhantomData,
+        count: 42,
+    };
+    let bytes = fory.serialize(&value).unwrap();
+    let result: StructWithPhantom = fory.deserialize(&bytes).unwrap();
+    assert_eq!(result, value);
+}
+
+/// Test struct containing multiple PhantomData fields with different types
+#[derive(Debug, PartialEq, ForyObject)]
+struct StructWithMultiplePhantom {
+    name: String,
+    _phantom1: PhantomData<String>,
+    count: i32,
+    _phantom2: PhantomData<Vec<u8>>,
+}
+
+#[test]
+fn test_struct_with_multiple_phantom_data() {
+    let mut fory = Fory::default();
+    fory.register::<StructWithMultiplePhantom>(101).unwrap();
+
+    let value = StructWithMultiplePhantom {
+        name: "test".to_string(),
+        _phantom1: PhantomData,
+        count: 42,
+        _phantom2: PhantomData,
+    };
+    let bytes = fory.serialize(&value).unwrap();
+    let result: StructWithMultiplePhantom = fory.deserialize(&bytes).unwrap();
+    assert_eq!(result, value);
+}
+
+/// Test nested struct with PhantomData
+#[derive(Debug, PartialEq, ForyObject)]
+struct InnerWithPhantom {
+    value: i32,
+    _marker: PhantomData<String>,
+}
+
+#[derive(Debug, PartialEq, ForyObject)]
+struct OuterWithPhantom {
+    inner: InnerWithPhantom,
+    name: String,
+    _marker: PhantomData<Vec<i32>>,
+}
+
+#[test]
+fn test_nested_struct_with_phantom_data() {
+    let mut fory = Fory::default();
+    fory.register::<InnerWithPhantom>(102).unwrap();
+    fory.register::<OuterWithPhantom>(103).unwrap();
+
+    let value = OuterWithPhantom {
+        inner: InnerWithPhantom {
+            value: 100,
+            _marker: PhantomData,
+        },
+        name: "outer".to_string(),
+        _marker: PhantomData,
+    };
+    let bytes = fory.serialize(&value).unwrap();
+    let result: OuterWithPhantom = fory.deserialize(&bytes).unwrap();
+    assert_eq!(result, value);
+}
diff --git a/rust/tests/tests/test_tuple.rs b/rust/tests/tests/test_tuple.rs
index 858067a5a..a28ea35c0 100644
--- a/rust/tests/tests/test_tuple.rs
+++ b/rust/tests/tests/test_tuple.rs
@@ -344,3 +344,46 @@ fn test_struct_with_complex_tuple_fields() {
 fn test_struct_with_complex_tuple_fields_xlang() {
     run_struct_with_complex_tuple_fields(true);
 }
+
+// Test unit type () - the empty tuple / 0-element tuple
+#[test]
+fn test_tuple_with_unit() {
+    let fory = Fory::default();
+
+    let value: (i32, (), String) = (42, (), "hello".to_string());
+    let bytes = fory.serialize(&value).unwrap();
+    let result: (i32, (), String) = fory.deserialize(&bytes).unwrap();
+    assert_eq!(result, value);
+}
+
+#[test]
+fn test_tuple_with_multiple_units() {
+    let fory = Fory::default();
+
+    let value: ((), i32, (), String, ()) = ((), 42, (), "hello".to_string(), 
());
+    let bytes = fory.serialize(&value).unwrap();
+    let result: ((), i32, (), String, ()) = fory.deserialize(&bytes).unwrap();
+    assert_eq!(result, value);
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct StructWithUnit {
+    name: String,
+    unit: (),
+    count: i32,
+}
+
+#[test]
+fn test_struct_with_unit_field() {
+    let mut fory = Fory::default();
+    fory.register::<StructWithUnit>(200).unwrap();
+
+    let value = StructWithUnit {
+        name: "test".to_string(),
+        unit: (),
+        count: 42,
+    };
+    let bytes = fory.serialize(&value).unwrap();
+    let result: StructWithUnit = fory.deserialize(&bytes).unwrap();
+    assert_eq!(result, value);
+}


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

Reply via email to