blob: cba76f701072d90f6749f5611c4359590b3f0bc1 [file] [view]
---
title: Native Serialization
sidebar_position: 3
id: native_serialization
license: |
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.
---
Rust native serialization is the Rust-only wire mode selected with `.xlang(false)`. Use it when
every writer and reader is Rust and the payload should preserve Rust object-graph behavior instead
of the portable xlang type system.
Use [Xlang Serialization](xlang-serialization.md), the default Rust mode, when bytes must be read
by Java, Python, C++, Go, JavaScript/TypeScript, C#, Swift, Dart, Scala,
Kotlin, or another non-Rust Fory runtime.
## When To Use Native Serialization
Use native serialization when:
- A payload is produced and consumed only by Rust applications.
- The data model uses Rust-specific object graph features such as `Rc<T>`, `Arc<T>`, weak
pointers, `RefCell<T>`, `Mutex<T>`, trait objects, or `dyn Any`.
- You want schema-consistent Rust payloads for lockstep services.
- You need compatible schema evolution for Rust-only rolling deployments.
- You want compile-time serializers from `#[derive(ForyStruct)]` without portable xlang mapping
constraints.
## Create a Native Runtime
```rust
use fory::{Error, Fory, ForyStruct};
#[derive(ForyStruct, Debug, PartialEq)]
struct Order {
id: i64,
amount: f64,
}
fn main() -> Result<(), Error> {
let mut fory = Fory::builder().xlang(false).build();
fory.register::<Order>(100)?;
let order = Order { id: 1, amount: 42.5 };
let bytes = fory.serialize(&order)?;
let decoded: Order = fory.deserialize(&bytes)?;
assert_eq!(order, decoded);
Ok(())
}
```
Perform registrations before sharing a `Fory` instance across threads. Once configured, `Fory` can
be shared through `Arc`.
## Schema Evolution
Native serialization defaults to schema-consistent mode. Enable compatible mode only when Rust-only
writer and reader versions can differ:
```rust
let mut writer = Fory::builder().xlang(false).compatible(true).build();
let mut reader = Fory::builder().xlang(false).compatible(true).build();
```
Compatible mode uses schema metadata to tolerate added, removed, or reordered fields when field
identity remains compatible. See [Schema Evolution](schema-evolution.md).
## Registration
Register application structs and enum-like types before serialization:
```rust
fory.register::<Order>(100)?;
fory.register_by_name::<Order>("example", "Order")?;
```
Use explicit numeric IDs for compact payloads and stable deployments. Use namespace/type-name
registration when independent teams coordinate type identity by names.
## Rust Object Surface
Native serialization owns the Rust-specific object surface:
- Structs and tuple structs with `#[derive(ForyStruct)]`.
- Enums and union-like models supported by Fory derive macros.
- `Vec`, maps, sets, tuples, arrays, and optional values.
- `Box<T>`, `Rc<T>`, `Arc<T>`, `RcWeak<T>`, and `ArcWeak<T>`.
- `RefCell<T>` and `Mutex<T>`.
- Trait objects such as `Box<dyn Trait>`, `Rc<dyn Trait>`, and `Arc<dyn Trait>`.
- Runtime type dispatch with `Rc<dyn Any>` and `Arc<dyn Any>`.
- Date and time carriers, including optional `chrono` support.
Use [Basic Serialization](basic-serialization.md), [References](references.md), and
[Trait Object Serialization](polymorphism.md) for focused examples.
## Shared And Circular References
Native mode can preserve shared references with `Rc<T>` and `Arc<T>`:
```rust
use fory::{Error, Fory};
use std::rc::Rc;
fn main() -> Result<(), Error> {
let fory = Fory::builder().xlang(false).build();
let shared = Rc::new(String::from("shared"));
let values = vec![shared.clone(), shared.clone()];
let bytes = fory.serialize(&values)?;
let decoded: Vec<Rc<String>> = fory.deserialize(&bytes)?;
assert!(Rc::ptr_eq(&decoded[0], &decoded[1]));
Ok(())
}
```
Use `.track_ref(true)` when weak pointers or explicit cyclic graphs need reference tracking:
```rust
let mut fory = Fory::builder().xlang(false).track_ref(true).build();
```
Weak pointers serialize as references to their target when the target is still alive, and as null
when the target has been dropped.
## Trait Objects
Trait objects are Rust runtime features and belong in native serialization:
```rust
use fory::{register_trait_type, Error, Fory, ForyStruct, Serializer};
trait Animal: Serializer {
fn name(&self) -> &str;
}
#[derive(ForyStruct)]
struct Dog {
name: String,
}
impl Animal for Dog {
fn name(&self) -> &str {
&self.name
}
}
register_trait_type!(Animal, Dog);
fn main() -> Result<(), Error> {
let mut fory = Fory::builder().xlang(false).compatible(true).build();
fory.register::<Dog>(100)?;
let value: Box<dyn Animal> = Box::new(Dog { name: "Milo".into() });
let bytes = fory.serialize(&value)?;
let decoded: Box<dyn Animal> = fory.deserialize(&bytes)?;
assert_eq!(decoded.name(), "Milo");
Ok(())
}
```
Register every concrete implementation that can appear behind the trait object.
## Performance Guidelines
- Reuse a configured `Fory` instance and register types before concurrent use.
- Keep native schema-consistent mode for lockstep Rust services.
- Enable `.compatible(true)` only when Rust-only schema evolution is required.
- Use derive-generated serializers for application structs.
- Use `.track_ref(true)` only for weak-pointer or cyclic graph scenarios that require it.
- Prefer concrete typed fields over `dyn Any` or trait objects on hot paths.
## Native And Xlang Comparison
| Requirement | Use native serialization | Use xlang serialization |
| ---------------------------------------- | ------------------------ | ----------------------- |
| Rust-only payloads | Yes | Optional |
| Non-Rust readers or writers | No | Yes |
| `Rc`, `Arc`, weak pointers | Yes | No |
| Trait objects and `dyn Any` | Yes | No |
| Schema-consistent same-language payloads | Yes | No |
| Compatible schema evolution by default | No | Yes |
| Portable type mapping across runtimes | No | Yes |
## Troubleshooting
### A non-Rust runtime cannot read the payload
The writer is using native serialization. Rebuild it with `.xlang(true)` and align type
registration with every peer runtime.
### A weak pointer fails to resolve
Use `.track_ref(true)` and make sure the target object is still alive when serialized. Dropped weak
targets deserialize as null.
### A trait object cannot deserialize
Register the trait mapping and every concrete implementation that can appear behind the trait
object.
### A rolling deployment fails after a field change
Native serialization defaults to schema-consistent mode. Use `.compatible(true)` on both writer and
reader when schemas can differ.
## Related Topics
- [Xlang Serialization](xlang-serialization.md) - Cross-runtime Rust payloads
- [Configuration](configuration.md) - Builder options
- [Basic Serialization](basic-serialization.md) - Object graph serialization
- [Shared & Circular References](references.md) - `Rc`, `Arc`, and weak pointers
- [Trait Object Serialization](polymorphism.md) - Trait objects and dynamic dispatch
- [Schema Evolution](schema-evolution.md) - Compatible mode