Mapping for Structured Types
Generated structs have public members. This design choice is intentional for
data-carrying types:
- Simplicity: Avoids the need for getter/setter methods in three variants (by-value, by-reference, by-mutable-reference) for each field.
- Ergonomics: Enables direct field access and struct literal syntax.
- Consistency: Matches Rust conventions for plain data structures.
- Flexibility: Allows partial initialization with
..Default::default(). - Partial moves: Enables moving individual fields out of a struct without consuming the entire value.
#![allow(unused)]
fn main() {
// Rust
let _ = MyStruct {
my_int: 5,
my_str: "my string".into(),
my_vec: vec![1, 2, 3],
};
}
In addition, this plays nicely alongside the Default trait: users can specify
some values, whilst defaulting the rest. For example:
#![allow(unused)]
fn main() {
let _ = MyStruct {
my_int: 5,
..Default::default()
};
}
The variables that were not named in such a construct will be filled in with
the values acquired from the default function, i.e. the default values
specified in IDL.
Derived Traits
All generated types (struct, enum, union, bitmask, exception)
automatically derive a set of Rust traits. Some traits are always derived, while
others are conditionally derived based on recursive type analysis.
Always Derived
The following traits are always derived for all generated types:
| Trait | Purpose |
|---|---|
Clone | Enables deep copying of values |
Debug | Enables formatting with {:?} |
PartialEq | Enables equality comparison (==, !=) |
PartialOrd | Enables ordering comparison (<, >, etc) |
Conditionally Derived
The compiler performs a recursive analysis of each type to determine whether additional traits can be derived. This analysis examines all members, parent types, and nested types to make the determination.
Triviality Analysis (Copy)
A type is considered trivial if it consists only of:
- Primitive types (
bool, integers,char) - Arrays of trivial types
- Other trivial user-defined types
A type is not trivial if it contains:
Stringor bounded/unbounded string typesVec(sequences)BTreeMap(maps)- Recursive/circular types
Any,Fixed, or interface types
Trivial types derive Copy, which enables bitwise copying without explicit
.clone() calls.
Total Order Analysis (Eq, Ord, Hash)
A type has total order if all its members can form a well-ordered set. This requires that all member types satisfy:
- The type is not a floating-point type (
f32,f64) - All nested/member types also have total order
Floating-point types break total order because NaN != NaN, which violates the
reflexivity property required by Eq.
Types with total order derive:
| Trait | Purpose |
|---|---|
Eq | Guarantees reflexive equality (x == x) |
Ord | Enables total ordering comparisons |
Hash | Enables use as keys in hash-based collections |
Default Trait
All generated types implement the Default trait. The implementation delegates
to the new() constructor, which initializes all members to their default
values (either from @default annotations or type-specific defaults).
#![allow(unused)]
fn main() {
impl Default for MyStruct {
fn default() -> Self {
Self::new()
}
}
}
Summary Table
| Trait | Condition |
|---|---|
Copy | Type is trivial (no heap allocation, not recursive) |
Clone | Always |
Debug | Always |
Default | Always (via new() constructor) |
Eq | Type has total order (no floating-point members) |
PartialEq | Always |
Ord | Type has total order (no floating-point members) |
PartialOrd | Always |
Hash | Type has total order (no floating-point members) |
Example
// IDL - trivial type with total order
struct Point {
int32 x;
int32 y;
};
// IDL - non-trivial type with total order
struct Person {
string name;
int32 age;
};
// IDL - non-trivial type without total order
struct Measurement {
float value;
string unit;
};
#![allow(unused)]
fn main() {
// Trivial + total order: derives Copy, Eq, Ord, Hash
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Point {
pub x: i32,
pub y: i32,
}
// Non-trivial + total order: derives Eq, Ord, Hash but not Copy
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Person {
pub name: String,
pub age: i32,
}
// Non-trivial + no total order: only base traits
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Measurement {
pub value: f32,
pub unit: String,
}
}
Example
// IDL
struct MyStruct {
unsigned long my_int;
string my_str;
sequence<int32> my_vec;
};
#![allow(unused)]
fn main() {
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct MyStruct {
pub my_int: u32,
pub my_str: String,
pub my_vec: Vec<i32>,
}
impl MyStruct {
pub fn new() -> Self {
Self {
my_int: 0,
my_str: String::new(),
my_vec: Vec::new(),
}
}
}
impl Default for MyStruct {
fn default() -> Self {
Self::new()
}
}
}
Nested Types
Nested or scoped types will be placed inside a module with the name of the parent scope.
// IDL
struct MyA {
struct MyB {
struct MyC {};
};
};
#![allow(unused)]
fn main() {
// Rust
struct MyA {};
pub mod my_a {
struct MyB {};
pub mod my_b {
struct MyC {};
};
};
}
Inheritance
Rust does not support inheritance. Following the IDL specification, fields from base types will be copied into the derived type.
// IDL
struct Parent {
int64 parent_field;
};
struct Derived : Parent {
int64 derived_field;
};
#![allow(unused)]
fn main() {
// Rust
pub struct Parent {
pub parent_field: i64,
}
pub struct Derived {
pub parent_field: i64,
pub derived_field: i64,
}
}