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]