rust.md

August 3, 2026 ยท View on GitHub

Output Layout

Rust output is one module file per schema, for example:

  • <rust_out>/addressbook.rs

When --grpc is used and the schema contains services, Rust also emits:

  • <rust_out>/addressbook_service.rs
  • <rust_out>/addressbook_service_grpc.rs

Type Generation

Unions map to Rust enums with #[fory(id = ...)] schema case attributes. #[fory(unknown)] Unknown(::fory::UnknownCase) marks the Fory-provided forward-compatibility carrier. The marker only selects the carrier and does not add an entry to the schema case table; schema cases still use the full 0..N ID range. A generated typed union must have at least one non-Unknown case. The compiler marks the first declared non-Unknown case as #[fory(default)]. When that case's payload implements Rust's standard Default trait, the compiler also emits a standard Default implementation from that case:

#[derive(::fory::ForyUnion, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Animal {
    #[fory(unknown)]
    Unknown(::fory::UnknownCase),
    #[fory(id = 0, default)]
    Dog(self::Dog),
    #[fory(id = 1)]
    Cat(self::Cat),
}

impl ::std::default::Default for Animal {
    fn default() -> Self {
        Self::Dog(<self::Dog as ::std::default::Default>::default())
    }
}

If the selected payload does not implement standard Default, such as an any payload, the generated union has no infallible Default implementation. This model-level default is independent of Fory's fallible deserialization default, which reconstructs the selected case through its codecs and active read context.

Nested types generate nested modules:

pub mod person {
    #[derive(ForyEnum, Debug, Clone, PartialEq, Default)]
    #[repr(i32)]
    pub enum PhoneType {
        #[default]
        Mobile = 0,
        Home = 1,
        Work = 2,
    }

    #[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
    pub struct PhoneNumber {
        #[fory(id = 1)]
        pub number: String,
        #[fory(id = 2)]
        pub phone_type: PhoneType,
    }
}

Messages derive ForyStruct and include to_bytes/from_bytes helpers:

#[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
pub struct Person {
    #[fory(id = 1)]
    pub name: String,
    #[fory(id = 7)]
    pub phones: Vec<person::PhoneNumber>,
    #[fory(id = 8)]
    pub pet: Animal,
}

Registration

Generated registration function:

pub fn register_types(fory: &mut Fory) -> Result<(), fory::Error> {
    fory.register_union::<Animal>(106)?;
    fory.register::<person::PhoneType>(101)?;
    fory.register::<person::PhoneNumber>(102)?;
    fory.register::<Person>(100)?;
    fory.register::<Dog>(104)?;
    fory.register::<Cat>(105)?;
    fory.register::<AddressBook>(103)?;
    Ok(())
}

For schemas without explicit [id=...], generated registration uses computed numeric IDs:

fory.register::<Status>(1124725126)?;
fory.register_union::<Wrapper>(1471345060)?;
fory.register::<Envelope>(3022445236)?;
fory.register_union::<envelope::Detail>(1609214087)?;
fory.register::<envelope::Payload>(2862577837)?;

If option enable_auto_type_id = false; is set:

fory.register_by_name::<Config>("myapp.models.Config")?;
fory.register_union_by_name::<Holder>("myapp.models.Holder")?;

Usage

let person = Person {
    name: "Alice".into(),
    pet: Animal::Dog(self::Dog::default()),
    ..Default::default()
};

let bytes = person.to_bytes()?;
let restored = Person::from_bytes(&bytes)?;

gRPC Service Companions

With --grpc, Rust emits <module>_service.rs for the async service trait and path constants, and <module>_service_grpc.rs for tonic clients, servers, codecs, and ForyGrpcPayload implementations backed by the model's to_bytes and from_bytes helpers. See Rust gRPC for dependencies and usage.