Apache Fory™ Rust
Apache Fory™ is a blazing fast multi-language serialization framework powered by JIT compilation and zero-copy techniques, providing up to ultra-fast performance while maintaining ease of use and safety.
The Rust implementation provides versatile and high-performance serialization with automatic memory management and compile-time type safety. It defaults to xlang mode for cross-language payloads; use native mode with .xlang(false) for Rust-only traffic and native-only data shapes. Trait objects and dyn Any also work in xlang mode when every selected concrete target has an xlang-compatible structural or EXT identity.
Why Apache Fory™ Rust?
- Blazingly Fast: Zero-copy deserialization and optimized binary protocols
- Cross-Language: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust
- Type-Safe: Compile-time type checking with derive macros
- Circular References: Automatic tracking of shared and circular references with
Rc/Arcand weak pointers - Polymorphic: Serialize trait objects with
Box<dyn Trait>,Rc<dyn Trait>, andArc<dyn Trait> - Schema Evolution: Compatible mode for independent schema changes
- Reduced-Precision Types:
Float16andBFloat16scalars withVec<Float16>/Vec<BFloat16>arrays - Two Formats: Object graph serialization and the Standard Row Format shared with Java, C++, and Python
Crates
| Crate | Description | Version |
|---|---|---|
fory |
User-facing API, runtime types, and derive macros | 1.6.0 |
fory-core |
Lower-level runtime crate for advanced integrations | 1.6.0 |
fory-derive |
Lower-level procedural macro crate for direct runtime use | 1.6.0 |
Most applications should depend on fory only. It re-exports the derive
macros and the public runtime types needed by generated code. Use fory-core
or fory-derive directly only when intentionally building on the lower-level
runtime crates.
Quick Start
Add Apache Fory™ to your Cargo.toml:
[]
= "1.6.0"
Basic Example
use ;
use ;
Core Features
1. Object Graph Serialization
Apache Fory™ provides automatic serialization of complex object graphs, preserving the structure and relationships between objects. The #[derive(ForyStruct)] macro generates efficient serialization code at compile time, eliminating runtime overhead.
Key capabilities:
- Nested struct serialization with arbitrary depth
- Collection types (Vec, HashMap, HashSet, BTreeMap)
- Optional fields with
Option<T> - Automatic handling of primitive types and strings
- Efficient binary encoding with variable-length integers
use ;
use ;
use HashMap;
let mut fory = builder.xlang.build;
fory..unwrap;
fory..unwrap;
let person = Person ;
let bytes = fory.serialize.unwrap;
let decoded: Person = fory.deserialize?;
assert_eq!;
2. Native-Mode Shared and Circular References
Apache Fory™ automatically tracks and preserves reference identity for shared objects using Rc<T> and Arc<T>. When the same object is referenced multiple times, Fory serializes it only once and uses reference IDs for subsequent occurrences. This ensures:
- Space efficiency: No data duplication in serialized output
- Reference identity preservation: Deserialized objects maintain the same sharing relationships
- Circular reference support: Use
RcWeak<T>andArcWeak<T>to break cycles
The examples in this section use native mode because Rc, Arc, and weak-pointer identity are
Rust object-graph features. Native mode stays on Rust's native type system instead of limiting the
payload to portable xlang mappings.
Shared References with Rc/Arc
use Fory;
use Rc;
let fory = builder.xlang.build;
// Create a shared value
let shared = new;
// Reference it multiple times
let data = vec!;
// The shared value is serialized only once
let bytes = fory.serialize?;
let decoded: = fory.deserialize?;
// Verify reference identity is preserved
assert_eq!;
assert_eq!;
// All three Rc pointers point to the same object
assert!;
assert!;
For thread-safe shared references, use Arc<T>.
Circular References with Weak Pointers
To serialize circular references like parent-child relationships or doubly-linked structures, use RcWeak<T> or ArcWeak<T> to break the cycle. These weak pointers are serialized as references to their strong counterparts, preserving the graph structure without causing memory leaks or infinite recursion.
How it works:
- Weak pointers serialize as references to their target objects
- If the strong pointer has been dropped, weak serializes as
Null - Forward references (weak appearing before target) are resolved via callbacks
- All clones of a weak pointer share the same internal cell for automatic updates
use ;
use ;
use RcWeak;
use Rc;
use RefCell;
let mut fory = builder.xlang.build;
fory.?;
// Build a parent-child tree
let parent = new;
let child1 = new;
let child2 = new;
parent.borrow_mut.children.push;
parent.borrow_mut.children.push;
// Serialize and deserialize the circular structure
let bytes = fory.serialize?;
let decoded: = fory.deserialize?;
// Verify the circular relationship
assert_eq!;
for child in &decoded.borrow.children
3. Trait Object Serialization
Apache Fory™ supports polymorphic serialization through trait objects, enabling dynamic dispatch and type flexibility. This is essential for plugin systems, heterogeneous collections, and extensible architectures.
The examples in this section use native mode. The same trait-object and dyn Any
carriers work in xlang mode when every selected concrete target has an
xlang-compatible structural or EXT identity; the Rust trait or erased-carrier
identity is not written to the wire.
Supported trait object types:
Box<dyn Trait>- Owned trait objectsRc<dyn Trait>- Reference-counted trait objectsArc<dyn Trait>- Thread-safe reference-counted trait objectsBox<dyn Any>/Rc<dyn Any>/Arc<dyn Any + Send + Sync>- Any trait type objectsVec<Box<dyn Trait>>,HashMap<K, Box<dyn Trait>>- Collections of trait objects
Box<dyn Any>, Rc<dyn Any>, and Arc<dyn Any + Send + Sync> are supported
erased Any carriers for registered concrete non-container payloads.
Use Arc<dyn Any + Send + Sync> when the erased payload must be shareable
across threads; the concrete payload type must also satisfy Send + Sync.
Registered structs, enums, and unions that satisfy those bounds can be used as
the erased payload.
Generic containers such as Vec<T>, HashMap<K, V>, HashSet<T>, and
LinkedList<T> are not supported directly as top-level erased Any payloads
behind any of those carriers. This also includes primitive vector encodings such
as Vec<u8>. Wrap the container in a registered derived type, or register an
exact-target custom serializer when an opaque EXT/NAMED_EXT representation is
appropriate.
Basic Trait Object Serialization Example:
use ;
// Register trait implementations
register_trait_type!;
let mut fory = builder.xlang.build;
fory.?;
fory.?;
fory.?;
let zoo = Zoo ;
let bytes = fory.serialize?;
let decoded: Zoo = fory.deserialize?;
assert_eq!;
assert_eq!;
4. Schema Evolution
Apache Fory™ supports schema evolution in Compatible mode, allowing serialization and deserialization peers to have different type definitions. Compatible mode is the default for both xlang and native mode. Set .compatible(false) only when every reader and writer always uses the same schema and you want faster serialization and smaller size. For xlang payloads, use .compatible(false) only after verifying that every language uses the same schema, or when native types are generated from Fory schema IDL.
Features:
- Add new fields with default values
- Remove obsolete fields (skipped during deserialization)
- Change field nullability (
T↔Option<T>) - Reorder fields (matched by name, not position)
- Type-safe fallback to default values for missing fields
Compatibility rules:
- Field names must match (case-sensitive)
- Type changes are not supported (except nullable/non-nullable)
- Nested struct types must be registered on both sides
use Fory;
use ;
use HashMap;
let mut fory1 = builder.xlang.build;
fory1..unwrap;
let mut fory2 = builder.xlang.build;
fory2..unwrap;
let person_v1 = PersonV1 ;
// Serialize with V1
let bytes = fory1.serialize.unwrap;
// Deserialize with V2 - missing fields get default values
let person_v2: PersonV2 = fory2.deserialize?;
assert_eq!;
assert_eq!;
assert_eq!;
5. Native-Mode Enum Support
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 variant ordinals
- Schema evolution support (add/remove variants, add/remove fields)
- Default variant support with
#[fory(default)] - Automatic type mismatch handling
use ;
let mut fory = builder.xlang.build;
fory.?;
let value = Object ;
let bytes = fory.serialize?;
let decoded: Value = fory.deserialize?;
assert_eq!;
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
#[fory(default)] - Named variants provide better evolution than unnamed
- Use compatible mode for cross-version communication
6. Native-Mode Tuple Support
Apache Fory™ supports tuples up to 22 elements out of the box with efficient serialization in both compatible mode and same-schema mode.
Features:
- Automatic serialization for tuples from 1 to 22 elements
- Heterogeneous type support (each element can be a different type)
- Schema evolution in Compatible mode (handles missing/extra elements)
Schema modes:
- Same-schema mode: Serializes elements sequentially without collection headers for minimal overhead
- Compatible mode: Uses collection protocol with type metadata for schema evolution
use ;
let mut fory = builder.xlang.build;
// Tuple with heterogeneous types
let data: = ;
let bytes = fory.serialize?;
let decoded: = fory.deserialize?;
assert_eq!;
7. Custom Serializers
For a type that needs an opaque encoding, implement a custom Serializer. A
separate serializer type can also target a type from another crate; see the
external-type serialization guide.
Custom serializers work in native and xlang modes when the chosen EXT identity
and opaque body format are supported by every peer. The example below uses
native mode.
- Types with special serialization requirements
- Existing data format compatibility
- Performance-critical opaque encoding
use ;
let mut fory = builder.xlang.build;
fory.?;
let point = Point ;
let bytes = fory.serialize?;
let decoded: Point = fory.deserialize?;
assert_eq!;
Custom serializers implement the body-only write_data and read_data
operations. Fory's complete-value write and read operations add reference
and type-information framing.
8. Standard Row Format
Apache Fory™ Rust implements the Standard Row Format shared with Java, C++, and Python. from_row returns a borrowed view, so applications can validate and access selected fields or collection elements without reconstructing the complete value.
Key benefits:
- Zero-copy access: Read strings, binary values, and nested structures through borrowed views
- Selective access: Read only the fields or collection elements the application needs
- Cross-language rows: Exchange the same binary layout with Java, C++, and Python
- Bounds-checked reads: Malformed offsets, sizes, counts, and UTF-8 return
Error
When to use row format:
- Analytics workloads with selective field access
- Large datasets where only a subset of fields is needed
- Memory-constrained environments
- High-throughput data pipelines
- Standard Row Format interchange with other Fory implementations
use ;
#[derive(ForyRow)] supports named structs, including generic structs, and encodes fields in source declaration order. Option<T> supplies field or array-element nullability without changing T's slot width. Generated field methods, array access and iteration, and map indexed access return Result, validating variable ranges and UTF-8 when accessed. Immutable views are cheap Copy values, and the RowView trait exposes their exact encoded slice through as_bytes.
Supported fixed-width values are bool, i8, i16, i32, i64, f32, f64, Date, Timestamp, and Duration. Supported variable-width values are UTF-8 String/&str, binary Vec<u8>/&[u8], fixed and dynamic arrays over supported element types, BTreeMap, nested derived structs, and Option<T>. Vec<u8> uses the binary encoding rather than the Standard Array encoding. Float16 and Decimal are not supported by Row Format because the standard specification does not define complete interoperable encodings for them.
Standard rows use an 8-byte-aligned null bitmap and one 8-byte slot per struct field. Fixed-width fields are stored little-endian in their slots. Variable-width slots encode the little-endian u64 value (relative_offset << 32) | size; variable bodies and array slot regions have zero padding to 8-byte alignment. Standard arrays use natural-width storage for fixed elements, and maps contain complete key and value arrays.
to_row accepts derived structs, supported arrays, and BTreeMap roots. to_row_into writes the same bytes into a reusable caller-owned Vec<u8> and clears partial output on error. Scalar, string, binary, and Option<T> values are field or element values rather than standalone roots. See the Rust Row Format guide and Row Format specification for details.
Cross-Language Serialization
Apache Fory™ supports seamless data exchange across multiple languages:
use Fory;
// Use xlang mode, the Rust default.
let mut fory = builder.xlang.build;
// Register types with consistent IDs across languages
fory.?;
// Or use name-based registration
fory.?;
See xlang_type_mapping.md for type mapping across languages.
Performance
Apache Fory™ Rust is designed for maximum performance:
- Selective Zero-Copy Access: Row Format returns borrowed views for direct field and element access
- Buffer Pre-allocation: Minimizes memory allocations during serialization
- Compact Encoding: Variable-length encoding for space efficiency
- Little-Endian: Optimized for modern CPU architectures
- Reference Deduplication: Shared objects serialized only once
Run benchmarks:
Documentation
- User Guide - Comprehensive user documentation
- API Documentation - Complete API reference
- Protocol Specification - Serialization protocol details
- Type Mapping - Cross-language type mappings
- Source - Source code for doc
Use Cases
Object Serialization
- Complex data structures with nested objects and references
- Cross-language communication in microservices
- General-purpose serialization with full type safety
- Schema evolution with compatible mode
- Graph-like data structures with circular references
Standard Row Format
- High-throughput data processing
- Analytics workloads requiring fast field access
- Memory-constrained environments
- Real-time data streaming applications
- Zero-copy scenarios
Development
Building
Testing
# Run all tests
# Run specific test
Code Quality
# Format code
# Check formatting
# Run linter
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Support
- Documentation: docs.rs/fory
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Slack: Apache Fory Slack
Apache Fory™ - Blazingly fast multi-language serialization framework.