blob: e2dcb49da6b7fa3aa8389cfb1143c65b4924e00f [file] [log] [blame] [view]
---
title: Shared & Circular References
sidebar_position: 5
id: references
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.
---
Apache Fory automatically tracks and preserves reference identity for shared objects using `Rc<T>` and `Arc<T>`.
## Shared References
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>` and `ArcWeak<T>` to break cycles
### Shared References with Rc
```rust
use fory::Fory;
use std::rc::Rc;
let fory = Fory::default();
// Create a shared value
let shared = Rc::new(String::from("shared_value"));
// Reference it multiple times
let data = vec![shared.clone(), shared.clone(), shared.clone()];
// The shared value is serialized only once
let bytes = fory.serialize(&data);
let decoded: Vec<Rc<String>> = fory.deserialize(&bytes)?;
// Verify reference identity is preserved
assert_eq!(decoded.len(), 3);
assert_eq!(*decoded[0], "shared_value");
// All three Rc pointers point to the same object
assert!(Rc::ptr_eq(&decoded[0], &decoded[1]));
assert!(Rc::ptr_eq(&decoded[1], &decoded[2]));
```
### Shared References with Arc
For thread-safe shared references, use `Arc<T>`:
```rust
use fory::Fory;
use std::sync::Arc;
let fory = Fory::default();
let shared = Arc::new(String::from("shared_value"));
let data = vec![shared.clone(), shared.clone()];
let bytes = fory.serialize(&data);
let decoded: Vec<Arc<String>> = fory.deserialize(&bytes)?;
assert!(Arc::ptr_eq(&decoded[0], &decoded[1]));
```
## 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.
**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
### Circular References with RcWeak
```rust
use fory::{Fory, Error};
use fory::ForyObject;
use fory::RcWeak;
use std::rc::Rc;
use std::cell::RefCell;
#[derive(ForyObject, Debug)]
struct Node {
value: i32,
parent: RcWeak<RefCell<Node>>,
children: Vec<Rc<RefCell<Node>>>,
}
let mut fory = Fory::default();
fory.register::<Node>(2000);
// Build a parent-child tree
let parent = Rc::new(RefCell::new(Node {
value: 1,
parent: RcWeak::new(),
children: vec![],
}));
let child1 = Rc::new(RefCell::new(Node {
value: 2,
parent: RcWeak::from(&parent),
children: vec![],
}));
let child2 = Rc::new(RefCell::new(Node {
value: 3,
parent: RcWeak::from(&parent),
children: vec![],
}));
parent.borrow_mut().children.push(child1.clone());
parent.borrow_mut().children.push(child2.clone());
// Serialize and deserialize the circular structure
let bytes = fory.serialize(&parent);
let decoded: Rc<RefCell<Node>> = fory.deserialize(&bytes)?;
// Verify the circular relationship
assert_eq!(decoded.borrow().children.len(), 2);
for child in &decoded.borrow().children {
let upgraded_parent = child.borrow().parent.upgrade().unwrap();
assert!(Rc::ptr_eq(&decoded, &upgraded_parent));
}
```
### Thread-Safe Circular Graphs with Arc
```rust
use fory::{Fory, Error};
use fory::ForyObject;
use fory::ArcWeak;
use std::sync::{Arc, Mutex};
#[derive(ForyObject)]
struct Node {
val: i32,
parent: ArcWeak<Mutex<Node>>,
children: Vec<Arc<Mutex<Node>>>,
}
let mut fory = Fory::default();
fory.register::<Node>(6000);
let parent = Arc::new(Mutex::new(Node {
val: 10,
parent: ArcWeak::new(),
children: vec![],
}));
let child1 = Arc::new(Mutex::new(Node {
val: 20,
parent: ArcWeak::from(&parent),
children: vec![],
}));
let child2 = Arc::new(Mutex::new(Node {
val: 30,
parent: ArcWeak::from(&parent),
children: vec![],
}));
parent.lock().unwrap().children.push(child1.clone());
parent.lock().unwrap().children.push(child2.clone());
let bytes = fory.serialize(&parent);
let decoded: Arc<Mutex<Node>> = fory.deserialize(&bytes)?;
assert_eq!(decoded.lock().unwrap().children.len(), 2);
for child in &decoded.lock().unwrap().children {
let upgraded_parent = child.lock().unwrap().parent.upgrade().unwrap();
assert!(Arc::ptr_eq(&decoded, &upgraded_parent));
}
```
## Supported Smart Pointer Types
| Type | Description |
| ------------ | --------------------------------------------------- |
| `Rc<T>` | Reference counting, shared refs tracked |
| `Arc<T>` | Thread-safe reference counting, shared refs tracked |
| `RcWeak<T>` | Weak reference to `Rc<T>`, breaks circular refs |
| `ArcWeak<T>` | Weak reference to `Arc<T>`, breaks circular refs |
| `RefCell<T>` | Interior mutability with runtime borrow checking |
| `Mutex<T>` | Thread-safe interior mutability |
## Best Practices
1. **Use Rc/Arc for shared data**: Let Fory handle deduplication
2. **Use weak pointers for cycles**: Prevent infinite recursion
3. **Prefer Arc for thread-safe scenarios**: When data crosses thread boundaries
4. **Combine with RefCell/Mutex**: For interior mutability
## Related Topics
- [Basic Serialization](basic-serialization.md) - Supported types
- [Polymorphism](polymorphism.md) - Trait objects with Rc/Arc
- [Configuration](configuration.md) - Reference tracking options