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.
🚀 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
- ⚡ Two Formats: Object graph serialization and zero-copy row-based format
📦 Crates
| Crate | Description | Version |
|---|---|---|
fory |
High-level API with derive macros | |
fory-core |
Core serialization engine | |
fory-derive |
Procedural macros |
🏃 Quick Start
Add Apache Fory™ to your Cargo.toml:
[]
= "0.14"
Basic Example
use ;
use ForyObject;
📚 Core Features
1. Object Graph Serialization
Apache Fory™ provides automatic serialization of complex object graphs, preserving the structure and relationships between objects. The #[derive(ForyObject)] 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 ForyObject;
use HashMap;
let mut fory = default;
fory.;
fory.;
let person = Person ;
let bytes = fory.serialize;
let decoded: Person = fory.deserialize?;
assert_eq!;
2. 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
Shared References with Rc/Arc
use Fory;
use Rc;
let fory = default;
// 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 ForyObject;
use RcWeak;
use Rc;
use RefCell;
let mut fory = default;
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.
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>- Any trait type objectsVec<Box<dyn Trait>>,HashMap<K, Box<dyn Trait>>- Collections of trait objects
Basic Trait Object Serialization Example:
use ;
use Serializer;
use ForyObject;
// Register trait implementations
register_trait_type!;
let mut fory = default.compatible;
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. This enables independent evolution of services in distributed systems without breaking compatibility.
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 ForyObject;
use HashMap;
let mut fory1 = default.compatible;
fory1.;
let mut fory2 = default.compatible;
fory2.;
let person_v1 = PersonV1 ;
// Serialize with V1
let bytes = fory1.serialize;
// Deserialize with V2 - missing fields get default values
let person_v2: PersonV2 = fory2.deserialize?;
assert_eq!;
assert_eq!;
assert_eq!;
5. 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
#[default] - Automatic type mismatch handling
use ;
let mut fory = default;
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
#[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.
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)
Serialization modes:
- Non-compatible 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 = default;
// Tuple with heterogeneous types
let data: = ;
let bytes = fory.serialize?;
let decoded: = fory.deserialize?;
assert_eq!;
7. Custom Serializers
For types that don't support #[derive(ForyObject)], implement the Serializer trait manually. This is useful for:
- External types from other crates
- Types with special serialization requirements
- Legacy data format compatibility
- Performance-critical custom encoding
use ;
use Any;
let mut fory = default;
fory.;
let custom = CustomType ;
let bytes = fory.serialize;
let decoded: CustomType = fory.deserialize?;
assert_eq!;
7. Row-Based Serialization
Apache Fory™ provides a high-performance row format for zero-copy deserialization. Unlike traditional object serialization that reconstructs entire objects in memory, row format enables random access to fields directly from binary data without full deserialization.
Key benefits:
- Zero-copy access: Read fields without allocating or copying data
- Partial deserialization: Access only the fields you need
- Memory-mapped files: Work with data larger than RAM
- Cache-friendly: Sequential memory layout for better CPU cache utilization
- Lazy evaluation: Defer expensive operations until field access
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
- Reading from memory-mapped files or shared memory
How it works:
- Fields are encoded in a binary row with fixed offsets for primitives
- Variable-length data (strings, collections) stored with offset pointers
- Null bitmap tracks which fields are present
- Nested structures supported through recursive row encoding
use ;
use ForyRow;
use BTreeMap;
let profile = UserProfile ;
// Serialize to row format
let row_data = to_row;
// Zero-copy deserialization - no object allocation!
let row = ;
// Access fields directly from binary data
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
// Access collections efficiently
let scores = row.scores;
assert_eq!;
assert_eq!;
assert_eq!;
let prefs = row.preferences;
assert_eq!;
assert_eq!;
assert_eq!;
Performance comparison:
| Operation | Object Format | Row Format |
|---|---|---|
| Full deserialization | Allocates all objects | Zero allocation |
| Single field access | Full deserialization required | Direct offset read |
| Memory usage | Full object graph in memory | Only accessed fields in memory |
| Suitable for | Small objects, full access | Large objects, selective access |
🌍 Cross-Language Serialization
Apache Fory™ supports seamless data exchange across multiple languages:
use Fory;
// Enable cross-language mode
let mut fory = default
.compatible
.xlang;
// Register types with consistent IDs across languages
fory.;
// Or use namespace-based registration
fory.;
See xlang_type_mapping.md for type mapping across languages.
⚡ Performance
Apache Fory™ Rust is designed for maximum performance:
- Zero-Copy Deserialization: Row format enables direct memory access without copying
- 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 documents
- 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
Row-Based Serialization
- 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.