This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to tag v0.13.2-rc1
in repository https://gitbox.apache.org/repos/asf/fory.git

commit e7754b964779b2c90208ca630e5df2f52a7976ad
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Nov 27 10:51:40 2025 +0800

    fix(rust): fix array field support (#2933)
    
    <!--
    **Thanks for contributing to Apache Fory™.**
    
    **If this is your first time opening a PR on fory, you can refer to
    
[CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).**
    
    Contribution Checklist
    
    - The **Apache Fory™** community has requirements on the naming of pr
    titles. You can also find instructions in
    [CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).
    
    - Apache Fory™ has a strong focus on performance. If the PR you submit
    will have an impact on performance, please benchmark it first and
    provide the benchmark result here.
    -->
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    
    ## What does this PR do?
    
    <!-- Describe the details of this PR. -->
    
    ## Related issues
    
    Fixes #2930
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 rust/fory-core/src/row/row.rs       | 41 ++++++++++++++++++++++++++
 rust/fory-derive/src/object/util.rs | 23 +++++++++++++--
 rust/tests/tests/test_array.rs      | 47 ++++++++++++++++++++++++++++++
 rust/tests/tests/test_row.rs        | 58 +++++++++++++++++++++++++++++++++++++
 4 files changed, 167 insertions(+), 2 deletions(-)

diff --git a/rust/fory-core/src/row/row.rs b/rust/fory-core/src/row/row.rs
index 6ef9ab222..0ee55a1c7 100644
--- a/rust/fory-core/src/row/row.rs
+++ b/rust/fory-core/src/row/row.rs
@@ -88,6 +88,47 @@ impl Row<'_> for bool {
     }
 }
 
+/// ArrayGetter for fixed-size arrays, wrapping the underlying ArrayViewer
+pub struct FixedArrayGetter<'a, T, const N: usize> {
+    array_data: ArrayViewer<'a>,
+    _marker: PhantomData<T>,
+}
+
+impl<'a, T: Row<'a>, const N: usize> FixedArrayGetter<'a, T, N> {
+    pub fn size(&self) -> usize {
+        self.array_data.num_elements()
+    }
+
+    pub fn get(&self, idx: usize) -> T::ReadResult {
+        if idx >= self.array_data.num_elements() {
+            panic!("out of bound");
+        }
+        let bytes = self.array_data.get_field_bytes(idx);
+        <T as Row>::cast(bytes)
+    }
+}
+
+impl<'a, T: Row<'a>, const N: usize> Row<'a> for [T; N] {
+    type ReadResult = FixedArrayGetter<'a, T, N>;
+
+    fn write(v: &Self, writer: &mut Writer) -> Result<(), Error> {
+        let mut array_writer = ArrayWriter::new(N, writer)?;
+        for (idx, item) in v.iter().enumerate() {
+            let callback_info = array_writer.write_start(idx);
+            <T as Row>::write(item, array_writer.get_writer())?;
+            array_writer.write_end(callback_info);
+        }
+        Ok(())
+    }
+
+    fn cast(row: &'a [u8]) -> Self::ReadResult {
+        FixedArrayGetter {
+            array_data: ArrayViewer::new(row),
+            _marker: PhantomData::<T>,
+        }
+    }
+}
+
 impl Row<'_> for NaiveDate {
     type ReadResult = Result<NaiveDate, Error>;
 
diff --git a/rust/fory-derive/src/object/util.rs 
b/rust/fory-derive/src/object/util.rs
index 2b50a7393..23e5b3140 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -229,6 +229,8 @@ pub(super) fn classify_trait_object_field(ty: &Type) -> 
StructField {
 pub(super) struct TypeNode {
     pub name: String,
     pub generics: Vec<TypeNode>,
+    /// For arrays, store the original type string "[T; N]" to preserve length 
info
+    pub original_type_str: Option<String>,
 }
 
 pub(super) fn try_primitive_vec_type(node: &TypeNode) -> Option<TokenStream> {
@@ -277,6 +279,14 @@ impl fmt::Display for TypeNode {
                     .collect::<Vec<_>>()
                     .join(", ")
             )
+        } else if self.name == "Array" {
+            // For arrays, use the original type string if available (e.g., 
"[f32; 4]")
+            if let Some(ref original) = self.original_type_str {
+                write!(f, "{}", original)
+            } else {
+                // Fallback - shouldn't happen if properly constructed
+                write!(f, "Array")
+            }
         } else if self.generics.is_empty() {
             write!(f, "{}", self.name)
         } else {
@@ -326,6 +336,7 @@ pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
         return TypeNode {
             name: "TraitObject".to_string(),
             generics: vec![],
+            original_type_str: None,
         };
     }
 
@@ -334,15 +345,19 @@ pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
         return TypeNode {
             name: "Tuple".to_string(),
             generics: vec![],
+            original_type_str: None,
         };
     }
 
-    // Handle arrays - extract element type
+    // Handle arrays - extract element type and preserve original type string
     if let Type::Array(array) = ty {
         let elem_node = parse_generic_tree(&array.elem);
+        // Preserve the original type string including the length (e.g., 
"[f32; 4]")
+        let original_type_str = quote!(#ty).to_string().replace(' ', "");
         return TypeNode {
             name: "Array".to_string(),
             generics: vec![elem_node],
+            original_type_str: Some(original_type_str),
         };
     }
 
@@ -368,7 +383,11 @@ pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
     } else {
         vec![]
     };
-    TypeNode { name, generics }
+    TypeNode {
+        name,
+        generics,
+        original_type_str: None,
+    }
 }
 
 pub(super) fn generic_tree_to_tokens(node: &TypeNode) -> TokenStream {
diff --git a/rust/tests/tests/test_array.rs b/rust/tests/tests/test_array.rs
index 8ee088549..76a8bf8b1 100644
--- a/rust/tests/tests/test_array.rs
+++ b/rust/tests/tests/test_array.rs
@@ -284,6 +284,53 @@ fn test_array_box_trait_objects() {
     }
 }
 
+#[test]
+fn test_vec_of_arrays() {
+    // Test from GitHub issue: Vec<[f32;4]> should work with ForyObject
+    let fory = Fory::default();
+
+    // Test Vec of primitive arrays
+    let points: Vec<[f32; 4]> = vec![
+        [1.0, 2.0, 3.0, 4.0],
+        [5.0, 6.0, 7.0, 8.0],
+        [9.0, 10.0, 11.0, 12.0],
+    ];
+
+    let bin = fory.serialize(&points).unwrap();
+    let result: Vec<[f32; 4]> = fory.deserialize(&bin).expect("deserialize");
+
+    assert_eq!(result.len(), 3);
+    assert_eq!(result[0], [1.0, 2.0, 3.0, 4.0]);
+    assert_eq!(result[1], [5.0, 6.0, 7.0, 8.0]);
+    assert_eq!(result[2], [9.0, 10.0, 11.0, 12.0]);
+}
+
+#[test]
+fn test_struct_with_vec_of_arrays() {
+    // Test from GitHub issue: struct with Vec<[f32;4]> field should work
+    #[derive(ForyObject, PartialEq, Debug)]
+    struct PointCloud {
+        index: i32,
+        points: Vec<[f32; 4]>,
+    }
+
+    let mut fory = Fory::default();
+    fory.register_by_name::<PointCloud>("PointCloud").unwrap();
+
+    let data = PointCloud {
+        index: 42,
+        points: vec![[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]],
+    };
+
+    let bin = fory.serialize(&data).unwrap();
+    let result: PointCloud = fory.deserialize(&bin).expect("deserialize");
+
+    assert_eq!(result.index, 42);
+    assert_eq!(result.points.len(), 2);
+    assert_eq!(result.points[0], [1.0, 2.0, 3.0, 4.0]);
+    assert_eq!(result.points[1], [5.0, 6.0, 7.0, 8.0]);
+}
+
 #[test]
 fn test_array_rc_trait_objects() {
     let mut fory = Fory::default().compatible(true);
diff --git a/rust/tests/tests/test_row.rs b/rust/tests/tests/test_row.rs
index 41ced830b..dce383099 100644
--- a/rust/tests/tests/test_row.rs
+++ b/rust/tests/tests/test_row.rs
@@ -20,6 +20,64 @@ use std::collections::BTreeMap;
 use fory_core::row::{from_row, to_row};
 use fory_derive::ForyRow;
 
+#[test]
+fn row_with_array_field() {
+    // Test from GitHub issue: ForyRow should work with fixed-size array fields
+    #[derive(ForyRow)]
+    struct PointWithArray {
+        index: i32,
+        point: [f32; 4],
+    }
+
+    let data = PointWithArray {
+        index: 42,
+        point: [1.0, 2.0, 3.0, 4.0],
+    };
+
+    let row = to_row(&data).unwrap();
+    let obj = from_row::<PointWithArray>(&row);
+
+    assert_eq!(obj.index(), 42);
+    let point_getter = obj.point();
+    assert_eq!(point_getter.size(), 4);
+    assert_eq!(point_getter.get(0), 1.0);
+    assert_eq!(point_getter.get(1), 2.0);
+    assert_eq!(point_getter.get(2), 3.0);
+    assert_eq!(point_getter.get(3), 4.0);
+}
+
+#[test]
+fn row_with_nested_struct_array() {
+    // Test ForyRow with nested struct containing arrays
+    #[derive(ForyRow)]
+    struct Point3D {
+        coords: [f64; 3],
+    }
+
+    #[derive(ForyRow)]
+    struct Geometry {
+        name: String,
+        origin: Point3D,
+    }
+
+    let data = Geometry {
+        name: String::from("origin"),
+        origin: Point3D {
+            coords: [0.0, 0.0, 0.0],
+        },
+    };
+
+    let row = to_row(&data).unwrap();
+    let obj = from_row::<Geometry>(&row);
+
+    assert_eq!(obj.name(), "origin");
+    let coords = obj.origin().coords();
+    assert_eq!(coords.size(), 3);
+    assert_eq!(coords.get(0), 0.0);
+    assert_eq!(coords.get(1), 0.0);
+    assert_eq!(coords.get(2), 0.0);
+}
+
 #[test]
 fn row() {
     #[derive(ForyRow)]


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

Reply via email to