生成 FIDL Rust crate
您可以通过以下两种方式从 FIDL 库生成 FIDL Rust crate:
- 使用 标准 FIDL 工具链。
- 自动 使用 Fuchsia 构建系统 (它在后台使用标准 FIDL 工具链)。只有 Fuchsia 源代码树中提供的相应参数。
库
FIDL library
映射到名为 fidl_
的 Rust 库 crate,后跟
包含下划线而不是点的完整库路径。
例如,假设存在 library
声明:
library fuchsia.examples;
相应的 FIDL crate 名为 fidl_fuchsia_examples
:
use fidl_fuchsia_examples as fex;
特征
FIDL Rust crate 中的一些方法和常量通过实现 FIDL 运行时 crate 中的 trait。若要访问它们,请按以下步骤操作: 您必须导入相应的 trait。最简单的方法是 从序言模块中一次性导入所有模块:
use fidl::prelude::*;
序言使用 as _
语法重新导出 trait,因此仅导入 glob
将特征纳入解析方法和常量的范围。无法导入
特征自身的名称。
常量
给定常量:
const BOARD_SIZE uint8 = 9;
const NAME string = "Tic-Tac-Toe";
FIDL 工具链会生成以下常量:
pub const BOARD_SIZE: u8
pub const NAME: &str
有关 FIDL 基元类型与 Rust 类型之间的对应关系,请参见 内置类型。
字段
本部分介绍 FIDL 工具链如何将 FIDL 类型转换为原生 类型。这些类型可以在汇总类型中显示为成员,也可以显示为 传递给协议方法。
内置类型
在下表中,如果和“已借”的变体后
“拥有的”type 是指在汇总类型中显示的类型(例如,
结构体字段或矢量元素的类型),而“借用”的则是类型引用
与将协议方法参数用作协议方法参数时显示的类型
(从客户端的角度)或响应元组值(从服务器的
)。归根结底是自有的和借用的
充分利用 Rust 的所有权模式当使用
类型 T
,则代理函数调用无需获取
拥有 T
的所有权,因此 FIDL 工具链需要生成借用版本的
T
。借用的版本通常使用 &mut
,因为类型 T
可能包含句柄,
在这种情况下,FIDL 绑定会在编码时将句柄清零,
用于修改输入。使用 &mut
代替所有权可让调用方执行以下操作:
如果输入值不包含句柄,请重复使用。
FIDL 类型 | Rust 类型 |
---|---|
bool |
bool |
int8 |
i8 |
int16 |
i16 |
int32 |
i32 |
int64 |
i64 |
uint8 |
u8 |
uint16 |
u16 |
uint32 |
u32 |
uint64 |
u64 |
float32 |
f32 |
float64 |
f64 |
array<T, N> |
&mut [T; N] (已借的)[T, N] (已拥有) |
vector<T>:N |
&[T] (已借用,当 T 为数字基元时)&mut dyn ExactSizeIterator (已借用)Vec (已拥有) |
string |
&str (已借的)String (已拥有) |
server_end:P |
fidl::endpoints::ServerEnd<PMarker> ,其中 PMarker 是此协议的标记类型。 |
client_end:P |
fidl::endpoints::ClientEnd<PMarker> 其中 PMarker 是此协议的标记类型。 |
zx.Handle |
fidl::Handle |
zx.Handle:S |
会使用对应的句柄类型。例如 fidl::Channel 或 fidl::Vmo |
用户定义的类型
位、枚举和表始终使用其生成的类型 T
进行引用。
结构体和联合可以是必需或可选,
语境或借用环境,这意味着有四种可能的
Rust 类型。对于指定的 struct T
或 union T
,类型如下:
拥有 | 借用 | |
---|---|---|
必需 | T |
&mut T |
可选 | Option<T> |
Option<&mut T> |
请求、响应和事件参数
当 FIDL 需要生成单个 Rust 类型来表示参数时, 请求、响应或事件(例如对于结果类型), 使用以下规则:
- 多个形参表示为形参类型的元组。
- 单个形参可仅使用形参的类型表示。
- 一组空参数使用单位类型
()
表示。
类型
块数
根据 bits 的定义:
type FileMode = strict bits : uint16 {
READ = 0b001;
WRITE = 0b010;
EXECUTE = 0b100;
};
FIDL 工具链会生成一组
已调用bitflags
FileMode
,带有 FileMode::READ
、FileMode::WRITE
和
FileMode::EXECUTE
。
bitflags
结构体还提供以下方法:
get_unknown_bits(&self) -> u16
:返回仅包含 来自此位值的未知成员。对于严格位, 标记为#[deprecated]
,始终返回 0。has_unknown_bits(&self) -> bool
:返回此值是否包含任何值 未知位。对于严格位,应将其标记为#[deprecated]
并始终返回false
。
生成的 FileMode
结构体始终具有完整的 #[derive]
集
规则。
用法示例:
let flags = fex::FileMode::READ | fex::FileMode::WRITE;
println!("{:?}", flags);
枚举
根据 enum 定义:
type LocationType = strict enum {
MUSEUM = 1;
AIRPORT = 2;
RESTAURANT = 3;
};
FIDL 工具链会使用指定的底层类型生成 Rust enum
,
如果未指定,则为 u32
:
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u32)]
pub enum LocationType {
Museum = 1,
Airport = 2,
Restaurant = 3,
}
使用以下方法:
from_primitive(prim: u32) -> Option<Self>
:返回枚举的Some
变体(如果有),否则为None
。into_primitive(&self) -> u32
:返回底层判别值。is_unknown(&self) -> bool
:返回此枚举是否未知的指示值。对于 strict 类型,标记为#[deprecated]
并始终返回false
。
如果 LocationType
为灵活,则以下规则
其他方法:
from_primitive_allow_unknown(prim: u32) -> Self
:创建 基于基元值进行枚举。unknown() -> Self
:返回占位符未知枚举值。如果枚举 包含标记有[Unknown]
的成员,则其值 此方法返回的包含指定未知成员的值。
生成的 LocationType
enum
始终具有完整的 #[derive]
集
规则。
用法示例:
let from_raw = fex::LocationType::from_primitive(1).expect("Could not create LocationType");
assert_eq!(from_raw, fex::LocationType::Museum);
assert_eq!(fex::LocationType::Restaurant.into_primitive(), 3);
为了提供来源兼容性,flexible枚举具有未知的
宏(而不是 _
)来匹配未知成员
模式。如需查看示例,请参阅 LocationTypeUnknown!()
宏的用法:
match location_type {
fex::LocationType::Museum => println!("museum"),
fex::LocationType::Airport => println!("airport"),
fex::LocationType::Restaurant => println!("restaurant"),
fex::LocationTypeUnknown!() => {
println!("unknown value: {}", location_type.into_primitive())
}
}
未知宏的作用与 _
模式相同,但可以配置为
扩展为详尽的匹配。这对于发现缺失的情况非常有用。
结构体
对于 struct 声明:
type Color = struct {
id uint32;
@allow_deprecated_struct_defaults
name string:MAX_STRING_LENGTH = "red";
};
FIDL 工具链会生成 Rust struct
:
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Color {
pub id: u32,
pub name: String,
}
生成的 Color
struct
遵循 #[derive]
规则。
用法示例:
let red = fex::Color { id: 0u32, name: "red".to_string() };
println!("{:?}", red);
联合体
根据 union 定义:
type JsonValue = strict union {
1: int_value int32;
2: string_value string:MAX_STRING_LENGTH;
};
FIDL 工具链会生成 Rust enum
:
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum JsonValue {
IntValue(i32),
StringValue(String),
}
使用以下方法:
ordinal(&self) -> u64
:返回活动变体的序数,例如 (在 FIDL 类型中指定的)。is_unknown(&self) -> bool
:返回此联合是否未知的返回结果。始终 针对非灵活联合类型返回false
。对于严格 则将其标记为#[deprecated]
并始终返回false
。
如果 JsonValue
为灵活,则以下规则
其他方法:
unknown_variant_for_testing() -> Self
:创建一个未知的联合值。这个 应仅在测试中使用。
生成的 JsonValue
enum
遵循 #[derive]
规则。
用法示例:
let int_val = fex::JsonValue::IntValue(1);
let str_val = fex::JsonValue::StringValue("1".to_string());
println!("{:?}", int_val);
assert_ne!(int_val, str_val);
灵活并集和未知变体
灵活联合会生成一个额外的变体来代表 未知的案例此变体被视为不公开,不应 直接引用。
为了提供来源兼容性,柔性联合体有一个
未知的宏,用于匹配未知成员,而不是
_
模式。如需查看示例,请参阅 JsonValueUnknown!()
宏的用法:
match json_value {
fex::JsonValue::IntValue(val) => println!("int: {}", val),
fex::JsonValue::StringValue(val) => println!("string: {}", val),
fex::JsonValueUnknown!() => println!("unknown ordinal: {}", json_value.ordinal()),
}
未知宏的作用与 _
模式相同,但可以配置为
扩展为详尽的匹配。这对于发现缺失的情况非常有用。
当包含未知变体的并集的 FIDL 消息被解码为
JsonValue
,JsonValue::is_unknown()
会返回 true。
灵活联合体有一个自定义 PartialEq
,可让未知变体的行为类似于
NaN:它们并不等于任何对象(包括其自身)。请参阅 RFC-0137:
舍弃 FIDL 中的未知数据,以获取关于此决定的背景信息。
在解码未知变体时,严格联合会失败。 灵活联合在解码未知变体时成功,但是 对它进行重新编码时会失败。
表格
根据 table 定义:
type User = table {
1: age uint8;
2: name string:MAX_STRING_LENGTH;
};
FIDL 工具链会生成包含可选成员的 struct
User
:
#[derive(Debug, PartialEq)]
pub struct User {
pub age: Option<u8>,
pub name: Option<String>,
#[doc(hidden)]
pub __source_breaking: fidl::marker::SourceBreaking,
}
如果在解码期间遇到任何未知字段,则会舍弃它们。那里 无法访问这些事件,也无法确定它们是否发生过。
__source_breaking
成员表示要详尽初始化表
添加新字段时会导致 API 中断。您应该使用
结构体更新语法,以使用 Default::default()
填充未指定字段。
例如:
let user = fex::User { age: Some(20), ..Default::default() };
println!("{:?}", user);
assert!(user.age.is_some());
同样,表也不允许进行详尽匹配。相反,您必须使用
..
语法,用于忽略未指定的字段。例如:
let fex::User { age, name, .. } = user;
生成的 User
struct
遵循 #[derive]
规则。
内嵌布局
生成的 Rust 代码使用 fidlc
预留的名称
内嵌布局
派生
当 FIDL 工具链为 FIDL 类型生成新的 struct
或 enum
时,
尝试从预定义的有用 trait 列表中 derive
的 trait 数量与
它可以包括 Debug
、Copy
、Clone
等。完整的特征列表
附录 A。
对于聚合类型(例如结构体、联合和表),派生集为
从所有可能派生的列表开始,然后从
一些基于类型中传递性存在的字段。例如:
以传递方式包含 vector
的汇总类型不会派生 Copy
,并且
包含 handle
的类型(即未标记为
resource
) 不会派生 Copy
和 Clone
。如有疑问,
参考生成的代码,检查哪些 trait 是由特定
类型。如需了解实现详情,请参阅附录 B。
协议
给定一个协议:
closed protocol TicTacToe {
strict StartGame(struct {
start_first bool;
});
strict MakeMove(struct {
row uint8;
col uint8;
}) -> (struct {
success bool;
new_state box<GameState>;
});
strict -> OnOpponentMove(struct {
new_state GameState;
});
};
与 TicTacToe
交互的主要入口点是 TicTacToeMarker
结构体,其中包含两个关联的类型:
Proxy
:与异步客户端搭配使用的关联代理类型。在本课中, 例如,这是生成的TicTacToeProxy
类型。同步客户端应 直接使用TicTacToeSynchronousProxy
(请参阅 同步),即不存储在 相关类型。TicTacToeMarker
RequestStream
:服务器的相关请求流 实现此协议所需处理的事务。在此示例中,TicTacToeRequestStream
:由 FIDL 生成。
此外,TicTacToeMarker
还包含以下关联常量:
实现 fidl::endpoints::ProtocolMarker
:
DEBUG_NAME: &’static str
:适合调试的服务的名称 目的。
系统可能会生成其他代码,具体取决于协议和方法 属性。
客户端
异步
对于异步客户端,FIDL 工具链会生成一个 TicTacToeProxy
结构体
替换为以下内容:
关联的类型:
TicTacToeProxy::MakeMoveResponseFut
:对相应事件的响应的Future
类型 双向方法。此类型会实现std::future::Future<Output = Result<(bool, Option<Box<GameState>>), fidl::Error>> + Send
。
方法:
new(channel: fidl::AsyncChannel) -> TicTacToeProxy
:为以下实例创建一个新代理TicTacToe
。take_event_stream(&self) -> TicTacToeEventStream
:获取Stream
的事件 (请参阅事件)。
实现 fidl::endpoints::Proxy
的方法:
from_channel(channel: fidl::AsyncChannel) -> TicTacToeProxy
:与TicTacToeProxy::new
。into_channel(self) -> Result<fidl::AsyncChannel>
:尝试转换 以便重新访问某个渠道as_channel(&self) -> &fidl::AsyncChannel
:获取对代理的 底层渠道is_closed(&self) -> bool
:检查代理是否已收到PEER_CLOSED
信号。on_closed<'a>(&'a self) -> fuchsia_async::OnSignals<'a>
:打造 在代理收到PEER_CLOSED
信号时完成。
实现 TicTacToeProxyInterface
的方法:
start_game(&self, mut start_first: bool) -> Result<(), fidl::Error>
:代理 方法。它将作为 请求参数,并返回空的结果。make_move(&self, mut row: u8, mut col: u8) -> Self::MakeMoveResponseFut
: 双向方法的代理方法。它将请求作为参数 参数,并返回响应的Future
。
请参阅 Rust 教程。
TicTacToeProxyInterface
trait 对于测试客户端代码很有用。对于
例如,如果您编写一个将 &T
作为参数的函数,其中 T:
TicTacToeProxyInterface
,则可以使用虚构代理类型对其进行单元测试:
use futures::future::{ready, Ready};
struct FakeTicTacToeProxy {
move_response: (bool, Option<Box<GameState>>),
}
impl TicTacToeProxyInterface for FakeTicTacToeProxy {
fn start_game(&self, mut start_first: bool) -> Result<(), fidl::Error> {
Ok(())
}
type MakeMoveResponseFut = Ready<fidl::Result<(bool, Option<Box<GameState>>)>>;
fn make_move(&self, mut row: u8, mut col: u8) -> Self::MakeMoveResponseFut {
ready(self.move_response.clone())
}
}
同步
对于 TicTacToe
协议的同步客户端,FIDL 工具链
使用以下方法生成 TicTacToeSynchronousProxy
结构体:
new(channel: fidl::Channel) -> TicTacToeSynchronousProxy
:返回一个新的 同步代理。假设服务器端 来实现TicTacToe
协议。into_channel(self) -> fidl::Channel
:将代理转换回通道。start_game(&self, mut a: i64) -> Result<(), fidl::Error>
:代理方法 触发后忘记方法:该方法将请求形参作为实参, 返回空结果。make_move(&self, mut row: u8, mut col: u8, __deadline: zx::MonotonicTime) -> Result<(bool, Option<Box<GameState>>), fidl::Error>
:适用于 2 的代理方法 Way 方法。该方法将请求形参作为实参,后跟 期限参数,该参数指示方法调用等待 响应(或zx::MonotonicTime::INFINITE
则无限期屏蔽)。它会返回一个 响应参数的Result
。wait_for_event(&self, deadline: zx::MonotonicTime) -> Result<TicTacToeEvent, fidl::Error>
:在收到事件或 截止时间到期(使用zx::MonotonicTime::INFINITE
可无限期屏蔽)。它会返回TicTacToeEvent
枚举的Result
。
请参阅 Rust 教程。
服务器
协议请求流
为了表示发送到服务器的传入请求流,FIDL 工具链
生成一个 TicTacToeRequestStream
类型,该类型会实现 futures::Stream<Item
= Result<TicTacToeRequest, fidl::Error>>
以及
fidl::endpoints::RequestStream
。每个协议都有相应的请求
流类型。
请求枚举
TicTacToeRequest
是一个枚举,表示
TicTacToe
协议。它具有以下变体:
StartGame { start_first: bool, control_handle: TicTacToeControlHandle }
:A 触发和遗忘请求,其中包含请求参数和控制句柄。MakeMove { row: u8, col: u8, responder: TicTacToeMakeMoveResponder }
:A 2 Way 方法请求,其中包含请求参数和 回复者。
系统会为每个协议生成一个这样的枚举。
请求回复者
每种双向方法都有一个生成的响应者类型,
来响应请求。在本示例中,只有一个双向
方法,FIDL 工具链会生成 TicTacToeMakeMoveResponder
,
提供以下方法:
send(self, mut success: bool, mut new_state: Option<&mut GameState>) -> Result<(), fidl::Error>
:发送响应。send_no_shutdown_on_err(self, mut success: bool, mut new_state: Option<&mut GameState>) -> Result<(), fidl::Error>
:与send
类似,但不会关闭 如果出现错误,则会沿信道关闭。control_handle(&self) -> &TicTacToeControlHandle
:获取底层 控制手柄。drop_without_shutdown(mut self)
:舍弃响应者而不关闭 频道
协议控制句柄
FIDL 工具链会生成 TicTacToeControlHandle
以封装客户端
TicTacToe
协议的端点。它包含
方法:
shutdown(&self)
:关闭频道。shutdown_with_epitaph(&self, status: zx_status::Status)
:发送摘要 然后再关闭这个频道send_on_opponent_move(&self, mut new_state: &mut GameState) -> Result<(), fidl::Error>
:事件的代理方法,以事件的 并返回一个空结果(请参阅 事件)。
事件
客户端
为了在异步客户端上接收事件,FIDL 工具链会生成
TicTacToeEventStream
,可使用 take_event_stream()
获取
方法(针对 TicTacToeProxy
)。
TicTacToeEventStream
会实现 futures::Stream<Item = Result<TicTacToeEvent,
fidl::Error>>
。
为了在同步客户端上接收事件,FIDL 工具链会生成
wait_for_event
方法(针对
TicTacToeSynchronousProxy
,它会返回一个
TicTacToeEvent
。
TicTacToeEvent
是表示可能事件的枚举。它具有
以下变体:
OnOpponentMove { new_state: GameState }
:TicTacToeEvent
事件。
并提供以下方法:
into_on_opponent_move(self) -> Option<GameState>
:返回Some
该事件的参数,或者为None
(如果 变体与方法调用不匹配。
服务器
服务器可以使用控制句柄发送事件
相应协议可通过
从客户端收到的 TicTacToeRequest
。对于触发和忘记方法,
可通过 control_handle
字段获取控制手柄,
方法,可通过响应程序上的 control_handle()
方法获得。
协议的控制句柄也可以通过相应的
请求流(在此示例中为 TicTacToeRequestStream
),因为它会实现
fidl::endpoints::RequestStream
。
结果
对于具有错误类型的方法:
protocol TicTacToe {
MakeMove(struct {
row uint8;
col uint8;
}) -> (struct {
new_state GameState;
}) error MoveError;
};
FIDL 工具链会为TicTacToeMakeMoveResult
std::result::Result<GameState, MoveError>
。下面介绍
此方法的生成方式与它具有单个响应参数 result
(即
类型 TicTacToeMakeMoveResult
。用于成功结果的类型如下:
参数类型转换规则。
未知互动处理
服务器端
当协议声明为 open
或 ajar
时,生成的请求
枚举将有一个名为
_UnknownMethod
,其中包含以下字段:
#[non_exhausitve]
_UnknownMethod {
/// Ordinal of the method that was called.
ordinal: u64,
/// Control handle for the protocol.
control_handle: TicTacToeControlHandle,
/// Enum indicating whether the method is a one-way method or a two way
/// method. This field only exists if the protocol is open.
method_type: fidl::MethodType,
}
MethodType
是一个包含两个单元变体(OneWay
和 TwoWay
)的枚举,
指明调用的是哪种方法
每当服务器收到灵活的未知事件时,请求流都会 发出请求枚举的这一变体。
客户端
客户端无法判断 flexible
单向方法是否已知
与服务器通信。对于 flexible
双向方法(如果方法未知)
发送至服务器,客户端将收到 Err
结果,值为
fidl::Error::UnsupportedMethod
。UnsupportedMethod
错误只可能出现
灵活的双向方法。
除了可能会发生 UnsupportedMethod
错误之外,
客户端上的 strict
和 flexible
方法之间的 API 差异。
对于 open
和 ajar
协议,生成的事件
枚举将有一个名为
_UnknownEvent
,其中包含以下字段:
#[non_exhaustive]
_UnknownEvent {
/// Ordinal of the event that was sent.
ordinal: u64,
}
每当客户端收到未知事件时,就会发出客户端事件流 事件枚举的这一变体。
协议组合
FIDL 没有继承的概念,它会生成完整的代码, (针对所有组合协议)执行上述操作。在 也就是为以下内容生成的代码:
protocol A {
Foo();
};
protocol B {
compose A;
Bar();
};
与以下代码相同:
protocol A {
Foo();
};
protocol B {
Foo();
Bar();
};
协议和方法属性
过渡风格
@transitional
属性仅影响 ProxyInterface
特征,即
有时用于测试代码。对于非测试代码,可以在
让请求处理程序暂时使用全包匹配实验组
在 Request
处理程序中。客户端代码不需要进行软转换
因为生成的代理将始终实现所有方法。
对于带有 @transitional
属性注解的方法,ProxyInterface
trait for 异步客户端提供
调用 unimplemented!()
的默认实现。如前所述,
对 Proxy
类型没有影响,该类型始终会实现所有 trait 的方法。
不过,当 ProxyInterface
特征处于
用于客户端单元测试中的虚构代理。
可检测到
对于使用 @discoverable
属性注释的协议,Marker 类型
此外,还会实现 fidl::endpoints::DiscoverableProtocolMarker
trait。
这会提供 PROTOCOL_NAME
关联的常量。
显式编码和解码
FIDL 消息在发送后会自动编码, 。您还可以显式编码和解码,例如 将 FIDL 数据复制到一个文件中。遵循 RFC-0120:单独使用 FIDL 线 格式时,这些绑定会提供一个持久性 API 和一个 独立 API。
持久性
显式编码和解码的推荐方法是使用 persist
和
unpersist
。这适用于非资源结构体、表和
并集。
例如,您可以保留 Color
结构体:
let original_value = fex::Color { id: 0, name: "red".to_string() };
let bytes = fidl::persist(&original_value)?;
之后再取消持久保留:
let decoded_value = fidl::unpersist(&bytes)?;
assert_eq!(original_value, decoded_value);
一般界面
对于不使用 Persistent API 的高级应用场景 您也可以使用独立的编码和解码 API。这适用于 所有结构体、表和联合。有两组函数:一组用于 value 类型,另外一个用于 resource 类型。
例如,由于 JsonValue
union 是一种值类型,因此我们对
使用 standalone_encode_value
:
let original_value = fex::JsonValue::StringValue("hello".to_string());
let (bytes, wire_metadata) = fidl::standalone_encode_value(&original_value)?;
这将返回一个字节矢量和一个用于存储传输格式的不透明对象
元数据。如需进行解码,请将这两个值传递给 standalone_decode_value
:
let decoded_value = fidl::standalone_decode_value(&bytes, &wire_metadata)?;
assert_eq!(original_value, decoded_value);
再举一个例子,考虑结构体 EventStruct
:
type EventStruct = resource struct {
event zx.Handle:<EVENT, optional>;
};
由于这是一种资源类型,我们使用
standalone_encode_resource
:
let original_value = fex::EventStruct { event: Some(fidl::Event::create()) };
let (bytes, handle_dispositions, wire_metadata) =
fidl::standalone_encode_resource(original_value)?;
除了字节和线路格式元数据之外,这还会返回一个
HandleDisposition
。如需解码,请将句柄处置方式转换为
HandleInfo
,然后将所有三个值传递给
standalone_decode_resource
:
let mut handle_infos = fidl::convert_handle_dispositions_to_infos(handle_dispositions)?;
let decoded_value: fex::EventStruct =
fidl::standalone_decode_resource(&bytes, &mut handle_infos, &wire_metadata)?;
assert!(decoded_value.event.is_some());
附录 A:派生特征
"Debug",
"Copy",
"Clone",
"Default",
"Eq",
"PartialEq",
"Ord",
"PartialOrd",
"Hash",
附录 B:填充派生
特征派生规则的计算显示在 fidlgen_rust:
// Calculates what traits should be derived for each output type,
// filling in all `*derives` in the IR.
func (c *compiler) fillDerives(ir *Root) {