Skip to content

[Rust] RefMode based Fine-Grained Ref Tracking for Rust #3097

@chaokunyang

Description

@chaokunyang

Feature Request

Add a RefMode enum to Rust similar to Go's implementation, and change all write_ref_info/read_ref_info boolean parameters in Serializer to accept RefMode, enabling fine-grained control of reference tracking per type and per field.

Is your feature request related to a problem? Please describe

Currently Rust uses a boolean write_ref_info/read_ref_info parameter which only distinguishes between "write ref flag" vs "don't write ref flag". This doesn't support:

  • Nullable types without ref tracking (like Option<T> where T is not a shared ref)
  • Explicit control over shared reference tracking per field via #[fory(ref=false)] attribute
  • Alignment with Go's RefMode design which has three modes: None, NullOnly, Tracking

Describe the solution you'd like

RefMode Enum Design

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum RefMode {
    #[default]
    None = 0,      // No ref flag written/read (primitives, non-nullable types)
    NullOnly = 1,  // Write NULL(-3) or NOT_NULL(-1), no ref tracking
    Tracking = 2,  // Full ref tracking with REF(-2) and REF_VALUE(0) flags
}

RefMode Semantics

RefMode Write Behavior Read Behavior
None No ref flag written No ref flag read
NullOnly Write -3 (null) or -1 (not null) Read flag, return default on null
Tracking Full ref tracking with all 4 flags Full ref resolution

Key Changes

  1. Serializer trait signature change:
fn fory_write(&self, context: &mut WriteContext, ref_mode: RefMode, write_type_info: bool, has_generics: bool) -> Result<(), Error>
fn fory_read(context: &mut ReadContext, ref_mode: RefMode, read_type_info: bool) -> Result<Self, Error>
  1. Per-field RefMode determination based on:

    • Type classification (primitive → None, Option → NullOnly, Rc/Arc → Tracking)
    • Field meta attributes: #[fory(ref=false)] to disable tracking, #[fory(nullable)] for null support
  2. Performance optimization for non-trackable types:

    • Use single if ref_mode != RefMode::None instead of match for primitives/strings
    • #[inline(always)] on hot paths

Describe alternatives you've considered

  1. Two boolean parameters (nullable: bool, tracking: bool): More verbose, 4 combinations when only 3 are needed
  2. Keep current boolean: Loses the ability to distinguish nullable-without-tracking from non-nullable

Additional context

Go Reference Implementation

type RefMode uint8
const (
    RefModeNone     RefMode = iota  // skip ref handling entirely
    RefModeNullOnly                  // only null check, no ref tracking
    RefModeTracking                  // full circular ref support
)

Protocol Specification (from xlang_serialization_spec.md)

Reference flags:

  • NULL FLAG (-3): Object is null
  • REF FLAG (-2): Object was already serialized (+ varint ref ID)
  • NOT_NULL VALUE FLAG (-1): Non-null, no ref tracking
  • REF VALUE FLAG (0): Referencable, first occurrence

Files to Modify

  • rust/fory-core/src/types.rs - Add RefMode enum
  • rust/fory-core/src/serializer/core.rs - Update Serializer trait
  • rust/fory-core/src/serializer/*.rs - Update all serializer implementations
  • rust/fory-derive/src/object/write.rs - Update generated code
  • rust/fory-derive/src/object/read.rs - Update generated code
  • rust/fory-derive/src/object/util.rs - Add determine_field_ref_mode() helper

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions