Highlights
- Dynamic Trait Object Serialization for Rust
- Shared/Circular ownership serialization for Rust
- Schema Forward/Backward compatibilify for Rust
- Drop-in Replacement for Python pickle: support local function/classes/
__reduce__/__getstate__serialization - Schema Forward/Backward compatibilify for Python dataclass
- Support codegen for xlang mode in java
- Primitive array compression using SIMD
- Compact Row Codec for Row Format
- Schema Forward/Backward compatibilify for Go
- Ahead-of-time codegen for golang struct serialization
Rust: First Release Highlights
This is the first Apache Fory Rust release, delivering a complete, high‑performance serialization stack. If you build Rust services or libraries, you can now use Fory natively with strong typing, performance, and schema evolution.
- Derive-based object graph serialization via
#[derive(ForyObject)] - Polymorphism for trait objects:
Box<dyn Trait>,Rc<dyn Trait>,Arc<dyn Trait>, plusdyn Any - Shared and circular reference tracking:
Rc/ArcandRcWeak/ArcWeak - Forward/backward compatible schema evolution (Compatible mode)
- Zero-copy Row format via
#[derive(ForyRow)]with selective field access - Thread-safe and multi-thread capable serialization (context pool)
- Broad collection support (e.g.,
VecDeque,LinkedList,BTreeMap,BTreeSet,BinaryHeap)
Quick start (minimal example):
use fory::{Fory, Error};
use fory::ForyObject;
#[derive(ForyObject, Debug, PartialEq)]
struct User {
name: String,
age: i32,
email: String,
}
fn main() -> Result<(), Error> {
let mut fory = Fory::default();
fory.register::<User>(1)?;
let user = User { name: "Alice".into(), age: 30, email: "alice@example.com".into() };
let bytes = fory.serialize(&user)?;
let decoded: User = fory.deserialize(&bytes)?;
assert_eq!(user, decoded);
Ok(())
}- Guide: Rust Serialization – https://fory.apache.org/docs/docs/guide/rust_serialization
- Crate: fory on crates.io – https://crates.io/crates/fory
- API docs: docs.rs – https://docs.rs/fory/latest/fory
Rust Benchmarks
Below are serialize throughput results (TPS; higher is better) comparing Fory with JSON and Protobuf across multiple datasets and sizes.
| Datatype | Size | Operation | Fory TPS | JSON TPS | Protobuf TPS | Fastest |
|---|---|---|---|---|---|---|
| company | small | serialize | 10,063,906 | 761,673 | 896,620 | fory |
| company | medium | serialize | 412,507 | 33,835 | 37,590 | fory |
| company | large | serialize | 9,183 | 793 | 880 | fory |
| ecommerce_data | small | serialize | 2,350,729 | 206,262 | 256,970 | fory |
| ecommerce_data | medium | serialize | 59,977 | 4,699 | 5,242 | fory |
| ecommerce_data | large | serialize | 3,727 | 266 | 295 | fory |
| person | small | serialize | 13,632,522 | 1,345,189 | 1,475,035 | fory |
| person | medium | serialize | 3,839,656 | 337,610 | 369,031 | fory |
| person | large | serialize | 907,853 | 79,631 | 91,408 | fory |
| simple_list | small | serialize | 27,726,945 | 4,874,957 | 4,643,172 | fory |
| simple_list | medium | serialize | 4,770,765 | 401,558 | 397,551 | fory |
| simple_list | large | serialize | 606,061 | 41,061 | 44,565 | fory |
| simple_map | small | serialize | 22,862,369 | 3,888,025 | 2,695,999 | fory |
| simple_map | medium | serialize | 2,128,973 | 204,319 | 193,132 | fory |
| simple_map | large | serialize | 177,847 | 18,419 | 18,668 | fory |
| simple_struct | small | serialize | 35,729,598 | 10,167,045 | 8,633,342 | fory |
| simple_struct | medium | serialize | 34,988,279 | 9,737,098 | 6,433,350 | fory |
| simple_struct | large | serialize | 31,801,558 | 4,545,041 | 7,420,049 | fory |
| system_data | small | serialize | 5,382,131 | 468,033 | 569,930 | fory |
| system_data | medium | serialize | 174,240 | 11,896 | 14,753 | fory |
| system_data | large | serialize | 10,671 | 876 | 1,040 | fory |
Note: Results depend on hardware, dataset, and implementation versions. See the Rust guide for how to run benchmarks yourself: https://github.com/apache/fory/blob/main/rust/benches/README.md
Python: Drop‑in Replacement for pickle
pyfory now acts as a high‑performance drop‑in replacement for pickle/cloudpickle, while keeping the same simple API and adding security and performance features.
- Serialize any Python object in Python‑native mode (
xlang=False): global/local functions, lambdas, global/local classes, instance/class/static methods - Honors Python hooks:
__getstate__,__setstate__,__reduce__,__reduce_ex__ - Reference tracking for shared/circular graphs with
ref=True - Pickle protocol 5 out‑of‑band buffers for zero‑copy via
pickle.PickleBuffer(NumPy, Pandas, etc.) - Security:
strict=Truefor registration‑based safety andDeserializationPolicyfor fine‑grained control - Threading:
ThreadSafeForyfor safe multi‑thread use - Familiar API:
dumps/loadsare aliases ofserialize/deserialize
Quick start:
import pyfory
# Drop-in replacement for pickle/cloudpickle
fory = pyfory.Fory(xlang=False, ref=True, strict=False)
def make_multiplier(k):
def mul(x):
return k * x
return mul
binary = fory.dumps(make_multiplier(10))
assert fory.loads(binary)(3) == 30Read more: Python Guide – https://fory.apache.org/docs/latest/python_serialization/
Features
- feat(java): support object stream serialization for graalvm by @chaokunyang in #2464
- refactor(java): rename abstract collection/map serializers to Map/ListLikeSerializer by @chaokunyang in #2466
- feat(memory): add customizable MemoryAllocator interface by @adriacabeza in #2467
- feat: Chain wheel test/build and release workflows by @esafak in #2483
- feat(python): set default languge to python for pyfory by @chaokunyang in #2490
- feat(python): add register api to python by @chaokunyang in #2491
- feat(python): meta compression for python by @chaokunyang in #2504
- feat(python): type meta encoding for python by @chaokunyang in #2509
- feat(CI): Cache npm, add node 24, lock file by @esafak in #2523
- feat(Rust): Implementing Type Compatible by @urlyy in #2492
- feat(Rust): support Option in MetaFieldType se/de by @urlyy in #2528
- feat(rust): support skipping fields bytes when deserializing in compatible mode by @urlyy in #2545
- feat(go): add type meta encoding for meta share by @junjiexh in #2554
- feat(Rust): Support automatic conversion between T and
Option<T>when deserialize by @urlyy in #2563 - feat(java): bean encoder implemented interfaces honor
@Ignoreby @stevenschlansker in #2576 - refactor(java): refactor fory java exception hierarchical structure by @chaokunyang in #2577
- feat(Go): Implement ahead of time codegen for fory-go serialization by @ThisingL in #2553
- feat(java): support limit deserialization depth by @chaokunyang in #2578
- feat(rust): add fory rust benchmark by @chaokunyang in #2583
- perf(rust): optimize rust deserialize perf by @chaokunyang in #2584
- feat(rust): add rust profiler for serialization by @chaokunyang in #2588
- refactor(go): rename FieldInfo to FieldDef to avoide name collision by @junjiexh in #2594
- feat(python): meta share mode for pyfory compatible serialization by @chaokunyang in #2593
- feat(java/python): align java and python compatible mode serialization by @chaokunyang in #2602
- feat(java/python): support enum xlang serialization by @chaokunyang in #2603
- feat(Rust): support basic type se/de aligned with java by @urlyy in #2585
- perf(python/java): Fix & optimize cross-language meta-share mode by @pandalee99 in #2601
- feat(go): align cross-language type serialization for primitive arrays by @pandalee99 in #2610
- feat(java): support codegen for xlang mode in java by @chaokunyang in #2613
- feat(java): primitive array compression using SIMD by @adriacabeza in #2485
- refactor(go): Replace globalTypeResolver with factory-based serializer registration by @ThisingL in #2615
- feat(go): Implement compatible mode with metashare mode by @junjiexh in #2607
- feat(java): support concurent map serialization when being updated by @chaokunyang in #2617
- feat(java): support concurrent updates when serializing collections by @chaokunyang in #2623
- feat(python): support limit pyfory depth by @chaokunyang in #2625
- feat(Rust): sort fields && feat Enum && fix read/write type_info && fix type_meta en/decode by @urlyy in #2630
- feat(python): drop-in replacement for pickle serialization by @chaokunyang in #2629
- refactor(java): refactor type resolver by @chaokunyang in #2640
- feat(java): support type converters for comaptible mode by @chaokunyang in #2641
- refactor(java/python): refine collection header bitmap by @chaokunyang in #2642
- feat(go): metashare mode support collection and map and nested object by @junjiexh in #2643
- feat(go): Add slice and map support to fory-go codegen serialization by @ThisingL in #2638
- refactor(go): Change codegen annotation from //fory:gen to //fory:generate by @ThisingL in #2648
- feat(rust): support Map and register_by_name by @urlyy in #2649
- feat(java): support graalvm 25 by @chaokunyang in #2652
- feat(java): support deserialize not registered/exsited class/fields for xlang compatible mode by @chaokunyang in #2655
- refactor(Rust): Refactor compile-time code & fix named_enum & fix skip enum by @urlyy in #2657
- feat(python): support local py class serialization by @chaokunyang in #2665
- refactor(go): refine collection header bitmap by @junjiexh in #2656
- feat(python): support class methods serialization by @chaokunyang in #2670
- refactor(go): refine collection header bitmap in codegen by @ThisingL in #2676
- feat(rust): support box serde for rust by @chaokunyang in #2677
- feat(rust): support reference tracking for rust Rc/Arc by @chaokunyang in #2678
- feat(python): refine python serialization api by @chaokunyang in #2673
- refactor(Rust): Refine api name by @urlyy in #2671
- feat(rust): support rust dyn trait object serialization by @chaokunyang in #2691
- feat(rust): support dyn any trait object serialization for box/arc/rc by @chaokunyang in #2704
- feat(rust): support shared reference tracking for
arc/rc<dyn T>by @chaokunyang in #2707 - feat(rust): avoid downcast method of multiple trait objects in same module conflict by @chaokunyang in #2708
- feat(rust): add deref to arc/rc wrapper by @chaokunyang in #2709
- refactor(rust): unify rc/arc wrapper macro arms into one function by @chaokunyang in #2711
- perf(Rust): Use SIMD to se/de string by @urlyy in #2716
- feat(Rust): named_xx se/de && ext se/de && add unittest by @urlyy in #2712
- feat(rust): support RcWeak/ArcWeak for circular reference tracking by @chaokunyang in #2714
- feat(rust): support limit max dyn depth by @chaokunyang in #2730
- feat(Rust): Unroll fields loop & Add a feature for this by @urlyy in #2724
- feat(python): make fory out-of-band serialization compatible with pickle5 by @chaokunyang in #2732
- refactor(go): Replace legacy RegisterTagType api call by @junjiexh in #2696
- feat(python): add thread safe fory by @chaokunyang in #2735
- feat(rust): support VecDeque/LinkedList serialization by @chaokunyang in #2741
- feat(rust): support btreemap serialization by @chaokunyang in #2743
- feat(rust): support btree set and binary heap serialization by @chaokunyang in #2744
- feat(Rust): support context_pool to reduce context allocation && support se/de in multi-thread by @urlyy in #2737
- feat(ci): cache Bazel binary in Python CI workflow by @SanyamSuyal in #2745
- feat(rust): rewrite fory derive macro for smaller and faster generated code using compile-time fields sort algorithm by @chaokunyang in #2749
- feat(ci): add maven cache to ci for faster build by @chaokunyang in #2751
- feat(rust): fast fory_read_compatible macro to use match by assigned field id by @chaokunyang in #2758
- refactor(rust): use compatible bool instead of enum to simplify API by @chaokunyang in #2763
- feat(rust): query type meta from parsed cache to speed up deserialization by @chaokunyang in #2764
- feat(java): introduce Compact Row Codec by @stevenschlansker in #2414
- feat(go): Add pointer field test for meta share mode by @junjiexh in #2674
- feat(rust): make Serializer api to return Result && replace panic/expect/assert/unwrap with Result by @urlyy in #2765
- feat(rust): refactor rust serialization system by @chaokunyang in #2774
- feat(python): support optional typehint for dataclass fields by @chaokunyang in #2766
- feat(rust): dynamic rust serializer system by @chaokunyang in #2778
- feat(rust): use rc instead of arc for type meta for faster performance by @chaokunyang in #2782
- feat(python): support dataclass compatible mode for python native mode by @chaokunyang in #2784
- feat(java): support deserialize non exist enum variant to default by @chaokunyang in #2787
- feat(go): update codegen field sorting to generate smaller and faster code by @ThisingL in #2779
- feat(rust): make type meta resolve return type info directly by @chaokunyang in #2789
- perf(Rust): remove clone()/to_owned() on MetaString/MetaStringBytes in MetaStringResolver to improve performance && fix xlang test by @urlyy in #2791
- feat(rust): support static struct version hash check for rust by @chaokunyang in #2793
- feat(rust): support profile all data types in rust benchmark by @chaokunyang in #2801
- perf(rust): optimize rust small string/struct read/write performance by @chaokunyang in #2803
- perf(rust): optimize rust performance by remove copy simd and add more inline hints by @chaokunyang in #2807
- perf(rust): always use utf8 when writing string by @chaokunyang in #2809
- feat(python): add deserialization policy for more fine-grained control and audit deserialization behaviour by @chaokunyang in #2811
- perf(Rust): Enchance performance by @theweipeng in #2810
- feat(Rust): Support serialize_to by @theweipeng in #2822
- feat(rust): lazy build typeinfo for rust to avoid nested struct register deps by @chaokunyang in #2824
- perf(Rust): Refactor reader by @theweipeng in #2826
- perf(python): optimize pyfory perf by @chaokunyang in #2829
Bug Fix
- fix(java): fix map/list element type same with collection type jit error by @chaokunyang in #2465
- fix(python): fix gh action pypi publish by @chaokunyang in #2468
- fix(java): fix row encoder for private struct by @chaokunyang in #2469
- fix(python): fix pyfory pypi release by @chaokunyang in #2473
- fix(python): fix py release on macos 13 by @chaokunyang in #2478
- fix(ci): Build python wheels using interpreters in manylinux2014 by @esafak in #2486
- fix(java): Encoders.mapEncoder(TypeRef, TypeRef, TypeRef, Fory) should load bean classes by @stevenschlansker in #2494
- fix(java): row format generated bean types handling Optional by @stevenschlansker in #2497
- fix: Fix the issue of addListener not releasing the lock by @open-snail in #2500
- fix(ci): Use $ROOT/dist for wheel distribution by @esafak in #2506
- fix(python): fix publich tag for workflow by @chaokunyang in #2517
- fix(ci): print workflow head branch by @chaokunyang in #2518
- fix(python): fix release python wheel head_branch tag by @chaokunyang in #2519
- fix(java): MemoryBuffer tests that equalTo works with size zero buffers by @stevenschlansker in #2524
- fix(ci): Pass tag to bump_py_version, set PLAT inside manylinux container by @esafak in #2522
- fix(ci): Use ref_name tag in Windows, name release runs, verify wheel versions, use all python versions for release by @esafak in #2532
- ci: Fix verify_version() by capturing version properly by @esafak in #2535
- fix(python): fix bump_wheel version by @chaokunyang in #2534
- fix(python): fix bump version by @chaokunyang in #2536
- fix(python): add macos 13 release by @chaokunyang in #2538
- fix(ci): use lowercase for betahuhn/repo-file-sync-action by @chaokunyang in #2541
- fix(ci): use commit hash for repo-file-sync-action by @chaokunyang in #2542
- fix(java): array encoder seems to waste 8 bytes in encode(T) by @stevenschlansker in #2551
- fix(ci): Install pyfory for golang xlang tests by @esafak in #2561
- fix(ci): Exit with subprocess return code in run_ci.py by @esafak in #2560
- fix(java): fix codegen name conflict by @chaokunyang in #2565
- fix(java): fix classinfo npe on graalvm by @chaokunyang in #2572
- fix(java): Skip calculation of classVersionHash if checkClassVersion is false by @theigl in #2573
- fix(java): skip hash compute for abstract field type by @chaokunyang in #2575
- fix(java): row format buffer recycling leaves offset and size for null values by @stevenschlansker in #2540
- fix(java): fix xlang register type by id by @chaokunyang in #2591
- fix(java): fix xlang mode meta shared for java by @chaokunyang in #2592
- fix(python): Fix the build dependency failure that occurred in pyfory by @pandalee99 in #2596
- fix(python): Delete the redundant code in typedef by @pandalee99 in #2600
- fix(java): fix register by id for xlang metashare mode in java by @chaokunyang in #2614
- fix(java/python): fix meta string encoding order by @chaokunyang in #2616
- fix(go): Complete type registration in factory-based serializer system by @ThisingL in #2619
- fix(python): fix pyfory depth limit by @chaokunyang in #2626
- fix(java): fix get nested genericType for codegen by @chaokunyang in #2632
- fix(python): register dynamic serializer only not require registration by @chaokunyang in #2634
- fix(java): fix record serialization on graalvm25 by @chaokunyang in #2644
- fix(java): fix field converter for xlang by @chaokunyang in #2654
- fix(java): fix array serialization on graalvm by @chaokunyang in #2664
- fix(java): fix meta share deserialization for register by @chaokunyang in #2667
- fix(java): retain serializers in graalvm native images by @gudzpoz in #2680
- fix(java): fix graalvm not use generated serializer by @chaokunyang in #2682
- fix(java): fix register serializer for xlang in java by @chaokunyang in #2683
- fix(java): fix InputStream read big buffer error by @wuwangben in #2687
- fix(python): fix python module/type serialize by @chaokunyang in #2688
- fix(java): Fix flakiness in CollectionSerializersTest#tesPriorityQueueSerializer by @swj20010308 in #2693
- fix(java): Fix flakiness in LazyMapTest#testMap by @swj20010308 in #2692
- fix(java): fix deserializing EXT type bug by @mengnankkkk in #2700
- fix(java): fix xlang typedef encode by @chaokunyang in #2719
- fix(java): Fix flakiness in MetaContextTest#testShareClassName and #testShareClassDefCompatible by @swj20010308 in #2739
- fix(java): Fix flakiness in ForyCopyTest#mutableObjectCopyTest by @swj20010308 in #2738
- fix(ci): add retry logic to Bazel downloads to prevent flaky CI failures by @SanyamSuyal in #2733
- fix(java): serialize collections with only null elements in xlang mode by @mymde in #2740
- fix(ci): fix sync file by @chaokunyang in #2750
- fix(ci): fix sync files by @chaokunyang in #2752
- fix(docs): fix rust doc links build and check by @chaokunyang in #2753
- fix(java): fix maven cache by @chaokunyang in #2755
- fix(rust): fix rust xlang tests by @chaokunyang in #2788
- fix(java): Fix flakiness in CollectionSerializersTest#testCollectionFieldSerializers, CollectionSerializersTest#testCollectionFieldSerializersCopy and ProtocolInteroperabilityTest#testComplexCollection by @swj20010308 in #2790
- fix(java): fix java shanpshot version by @chaokunyang in #2800
- fix(go): Adjust serialization of nullable value to align python behavior by @junjiexh in #2814
Other Improvements
- chore(python): disable pyfory.format import warning by @chaokunyang in #2476
- chore: bump release version to 0.12.0 by @chaokunyang in #2489
- docs: fix meta_size_mask by @urlyy in #2495
- chore: fix typos by @Asnowww in #2496
- docs: Improve pyfory PyPI documentation by @esafak in #2498
- docs(python): add row format doc by @chaokunyang in #2499
- chore: translate Chinese comments into English by @Asnowww in #2503
- docs: Improve pull request template by @esafak in #2508
- ci: Fix Windows wheel creation by @esafak in #2511
- ci: Pass the GH token to actions/download-artifact by @esafak in #2514
- chore: fix MetaStrEncoding by @co63oc in #2516
- chore(javascript): rename foryjs to apache-fory and remove install docs by @chaokunyang in #2544
- chore: bump release version to 0.12.1 by @chaokunyang in #2556
- docs: update contributing.md with Rust lint command by @urlyy in #2569
- chore(ci): use str replace to make bump java version faster by @chaokunyang in #2568
- docs(java): add idea jdk11 usage config by @chaokunyang in #2570
- docs(rust): add rust document by @chaokunyang in #2582
- chore: bump release version to 0.12.2 by @chaokunyang in #2589
- chore: add trademark for fory name by @chaokunyang in #2590
- chore: Revert "fix(python): Fix the build dependency failure that occurred in pyfory" by @chaokunyang in #2597
- chore: remove slash in homepage URL by @chaokunyang in #2604
- chore(go): replace all typ to type_ by @chaokunyang in #2612
- chore: bump release version to 0.12.3 by @chaokunyang in #2645
- docs: add AGENT.md to make AI coding more efficient by @chaokunyang in #2646
- chore(rust): remove tag read/write by @chaokunyang in #2681
- docs(python): refine python readme with detailed docs by @chaokunyang in #2689
- docs(python): update schema evolution support in README by @chaokunyang in #2690
- docs: fix python readme lint by @chaokunyang in #2694
- docs(rust): add comprehensive rust readme document by @chaokunyang in #2723
- docs(rust): add detailed rust api doc and readme by @chaokunyang in #2728
- docs: sync rust and python document to fory-site by @chaokunyang in #2734
- docs(rust): update rust roadmap by @chaokunyang in #2754
- docs(rust): fix rust doc broken link by @chaokunyang in #2756
- docs(rust): fix rust doc lint by @chaokunyang in #2757
- docs(go): Update README.md by @junjiexh in #2675
- docs(rust): add rust thread safety and troubleshooting doc by @chaokunyang in #2781
- chore(rust): rename fory-tests to tests by @chaokunyang in #2783
- docs(rust): update rust serializer trait doc by @chaokunyang in #2795
- chore: fix release error by @chaokunyang in #2796
- chore(python): rename numeric typehint by @chaokunyang in #2808
- chore(rust): reorder fory rust methods by @chaokunyang in #2813
- docs(python): refine python code and add api doc by @chaokunyang in #2816
- chore(ci): remove macos13 ci for rust/c++ by @chaokunyang in #2817
- chore(rust): rename metastring to meta_string by @urlyy in #2812
- docs(rust): add serialize_to api doc by @chaokunyang in #2825
- chore(Rust): Streamline code by @theweipeng in #2828
- docs(rust): update serialize to deserialize from doc by @chaokunyang in #2830
- chore(Rust): Remove new_from_fory from context.rs by @urlyy in #2831
New Contributors
- @adriacabeza made their first contribution in #2467
- @Asnowww made their first contribution in #2496
- @open-snail made their first contribution in #2500
- @junjiexh made their first contribution in #2554
- @ThisingL made their first contribution in #2553
- @gudzpoz made their first contribution in #2680
- @wuwangben made their first contribution in #2687
- @swj20010308 made their first contribution in #2693
- @mengnankkkk made their first contribution in #2700
- @SanyamSuyal made their first contribution in #2733
- @mymde made their first contribution in #2740
Full Changelog: v0.12.3...v0.13.0-rc2