本部分包含与 Fuchsia 接口定义语言文件相关的样式信息。
另请参阅 FIDL API 评分标准。
姓名
猫的命名是一件难事,
它可不是你放假时玩的游戏;
--- T.S. 艾略特
在 FIDL 中定义的名称用于在每种目标语言中生成标识符。 有些语言会为各种形式的名称附加语义或约定俗成的含义。例如,在 Go 中,标识符的首字母是否大写会控制标识符的可见性。因此,许多语言后端都会转换库中的名称,使其更适合目标语言。本部分中的命名规则旨在平衡 FIDL 源代码的可读性、每种目标语言的可用性以及各种目标语言之间的一致性。
避免使用常用的保留字,例如 goto。语言后端会将保留字转换为非保留标识符,但这些转换会降低这些语言的可用性。避免使用常用的保留字可减少应用这些转换的频率。
虽然某些 FIDL 关键字也是目标语言中的常用保留字(例如 C 和 C++ 中的 struct),因此应避免使用,但其他 FIDL 关键字(尤其是 request 和 handle)通常具有描述性,可以根据需要使用。
名称不得包含前导或尾随下划线。在某些语言中,前导或后导下划线具有语义含义(例如,前导下划线用于控制 Dart 中的可见性),而在其他语言中,它们具有常规含义(例如,后导下划线通常用于 C++ 中的成员变量)。此外,FIDL 编译器使用前导和后导下划线来混杂标识符,以避免发生冲突。
使用术语 size 来表示字节数。使用术语 count 来命名其他数量(例如,结构体向量中的项数)。
支持请求定义
有时,在决定如何分隔标识符中的字词时,有多种方法可供选择。我们的风格如下:
- 首先输入原始的美国英语短语(例如 “非 null HTTP 客户端”)
- 移除所有标点符号。(“非 Null HTTP 客户端”)
- 将所有内容都设为小写 (“non null http client”)
- 根据给定标识符的适用样式,执行以下操作之一:
- 将空格替换为下划线 ('_'),以实现小写蛇形命名法 (
non_null_http_client)。 - 将空格替换为下划线,并改为大写,以形成大写蛇形命名法 (
NON_NULL_HTTP_CLIENT)。 - 将每个字词的首字母大写,并将所有字词连接在一起,以形成驼峰式大小写 (
NonNullHttpClient)。
- 将空格替换为下划线 ('_'),以实现小写蛇形命名法 (
用法
下表将使用情形与元素对应起来:
| 元素 | 大小写 | 示例 |
|---|---|---|
bits |
大驼峰命名法 | InfoFeatures |
| 位字段成员 | 大写蛇形命名法 | WLAN_SNOOP |
const |
大写蛇形命名法 | MAX_NAMES |
alias |
大驼峰命名法 | DeviceId |
protocol |
大驼峰命名法 | AudioRenderer |
| 协议方法参数 | 小写蛇形命名法 | enable_powersave |
| 协议方法 | 大驼峰命名法 | GetBatteryStatus |
struct |
大驼峰命名法 | KeyboardEvent |
| 结构体成员 | 小写蛇形命名法 | child_pid |
table |
大驼峰命名法 | ComponentDecl |
| 表格成员 | 小写蛇形命名法 | num_rx |
union |
大驼峰命名法 | BufferFormat |
| 工会会员 | 小写蛇形命名法 | vax_primary |
enum |
大驼峰命名法 | PixelFormat |
| 枚举成员 | 大写蛇形命名法 | RGB_888 |
库
库名称是以英文句点分隔的标识符列表。库名称中除最后一部分之外的部分也称为命名空间。名称的每个组成部分都采用小写形式,并且必须与以下正则表达式匹配:[a-z][a-z0-9]*。
我们之所以使用这些限制性规则,是因为不同的目标语言对命名空间、库或软件包的限定方式有不同的限制。我们选择了一个保守的最小公分母,以便 FIDL 能够很好地与当前的目标语言集以及潜在的未来目标语言配合使用。
标识符名称:首选具有含义的功能角色
最好使用功能性名称(例如,fuchsia.media) 超过产品或代码名称(例如 fuchsia.amber 或 fuchsia.scenic)。当产品在 Fuchsia 之外具有某种外部存在,并且协议是该产品特有的时,产品名称是合适的。例如,fuchsia.cobalt 比 fuchsia.metrics 更适合作为 Cobalt 接口协议的名称,因为其他指标实现(例如,Firebase)不太可能实现相同的协议。
标识符名称应与参与者所扮演的特定角色相关;避免将访问权限控制编码到名称中。基于角色的名称具有描述性,并且不会像基于访问权限控制的名称那样快速过时,后者规定了外部定义的关系,而这种关系会随着平台的发展而变化。例如,对于涉及 FocusChain 对象的 API,合适的名称应为 fuchsia.ui.focus,而不是 fuchsia.ui.privileged;如果我们决定让 FocusChain 对象更广泛地可供访问,那么 fuchsia.ui.focus 就不是一个有问题的名称。应避免使用以下示例字词:
constrainedlimitedoemprivateprivilegedprotectedspecialvendor
标识符名称应具有含义;避免使用无意义的名称。如果 fuchsia.foo.bar 和 fuchsia.foo.baz 共享许多您希望分解到单独库中的概念,请考虑在 fuchsia.foo 中定义这些概念,而不是在 fuchsia.foo.common 中定义。应避免使用以下示例字词:
commonserviceutilbasef<letter>lzx<word>
顶层
避免重复库名称中的名称。例如,在 fuchsia.process 库中,启动进程的协议应命名为 Launcher 而不是 ProcessLauncher,因为名称 process 已出现在库名称中。在所有目标语言中,顶级名称都以某种方式通过库名称进行限定。
原初别名
基元别名不得重复封闭库中的名称。在所有目标语言中,原始别名都会替换为底层原始类型,因此不会导致名称冲突。
alias vaddr = uint64;
常量
常量名称不得与封闭库中的名称重复。在所有目标语言中,常量名称都由其封装库确定范围。
描述下限和上限的常量应分别使用前缀 MIN_ 和 MAX_。
const MAX_NAMES uint64 = 32;
协议
协议使用 protocol 关键字指定。
协议必须是名词短语。
通常,协议的命名会使用表示操作的名词。例如,AudioRenderer 是一个名词,表示该协议与呈现音频有关。同样,Launcher 是一个名词,表示该协议与启动某项事物有关。协议也可以是被动名词,尤其是当它们与实现所保持的某种状态相关时。例如,Directory 是一个名词,表示该协议用于与实现所持有的目录进行交互。
可以使用面向对象的设计模式来命名协议。例如,fuchsia.fonts.Provider 使用 provider 后缀,表示该协议提供字体(而不是表示字体本身)。同样,fuchsia.tracing.Controller 使用 controller 后缀,表示该协议控制跟踪系统(而不是表示跟踪本身)。
名称 Manager 可用作范围广泛的协议的最后一种名称。例如,fuchsia.power.Manager。不过,请注意,“管理器”协议往往会吸引大量松散相关的功能,这些功能最好分解为多个协议。
协议不得包含名称 service. 所有协议都定义服务。
该术语毫无意义。例如,fuchsia.tts.TtsService 在两个方面违反了此评分标准。首先,Tts 前缀与库名称重复。其次,禁止使用 Service 后缀。
“open/ajar/closed”露骨内容修饰符
对于协议,应始终指定 open、ajar 或 closed,而不是依赖默认值。也就是说,始终优先选择 open protocol Foo {
... 而不是仅选择 protocol Foo { ...。
方法
方法必须是动词短语。
例如,GetBatteryStatus 和 CreateSession 是动词短语,用于指明方法执行的操作。
当事件发生时调用的 listener 或 observer 协议中的方法应以 On 为前缀,并以过去时态描述发生的事件。例如,ViewContainerListener 协议有一个名为 OnChildAttached 的方法。
事件
同样,事件(即从服务器到客户端的未经请求的消息)应以 On 为前缀,并以过去时态描述发生的事件。
例如,AudioCapturer 协议有一个名为 OnPacketCaptured 的事件。
单方法协议
单个方法协议的方法应该是定义它们的协议名词短语的动词短语,例如 Loader.Load、Getter.Get、Uploader.Upload。对于限定性名词短语(例如 JobCreator 或 ProcessStopper),应使用非限定性动词短语,即 JobCreator.Create 或 ProcessStopper.Stop。
如果协议是单方法协议,但打算随着时间的推移发展为多方法协议,则不一定需要遵循此命名惯例。也就是说,如果协议存在已知扩展,但建议的命名不合适,那么最好尽早选择其他名称。如有疑问,应优先遵循默认建议。
由于替换协议比演进协议更难,因此如果 API 最初不打算演进,但最终发现需要迁移到多方法协议,最好通过添加方法(可能还需要重命名现有方法)来演进现有协议。
露骨内容“strict/flexible”修饰符
对于方法和事件,应始终指定 strict 或 flexible,而不是依赖默认值。也就是说,始终优先选择 flexible Foo(); 而不是仅选择 Foo()。
结构体、并集和表
结构、联合和表必须是名词短语。
例如,Point 是一个用于定义空间中位置的结构体,而 KeyboardEvent 是一个用于定义键盘相关事件的结构体。
结构体、并集和表成员
在实际应用中,最好使用单字词作为结构体、并集和表成员名称(单字词名称在各种目标语言中呈现的效果更一致)。 不过,如果单个字词会造成歧义或令人困惑,请大胆使用多个字词。
成员名称不得重复封闭类型(或库)中的名称,除非在没有封闭类型中的名称的情况下,成员名称会产生歧义。例如,包含事件传递时间的 KeyboardEvent 类型成员应命名为 time,而不是 event_time,因为名称 event 已出现在封装类型的名称中。在所有目标语言中,成员名称都由其封闭类型确定范围。
不过,如果类型为 DeviceToRoom(将智能设备与其所在的房间相关联),则可能需要具有成员 device_id 和 room_name,因为 id 和 name 含义不明确。这两个选项均可指设备或房间。
枚举
枚举必须是名词短语。
例如,PixelFormat 是一个枚举,用于定义如何将颜色编码为图片中的位。
枚举成员
枚举成员名称不得重复封装类型(或库)中的名称。例如,PixelFormat 枚举的成员应命名为 ARGB 而不是 PIXEL_FORMAT_ARGB,因为名称 PIXEL_FORMAT 已出现在封闭类型的名称中。在所有目标语言中,枚举成员名称都由其封闭类型限定范围。
位字段
位字段必须是名词短语。
例如,InfoFeatures 是一个位字段,用于指示以太网接口上存在哪些功能。
位字段成员
位域成员不得重复使用封装类型(或库)中的名称。例如,InfoFeatures 位域的成员应命名为 WLAN,而不是 INFO_FEATURES_WLAN,因为名称 INFO_FEATURES 已出现在封装类型的名称中。
在所有目标语言中,位字段成员名称都由其封闭类型确定范围。
类型
露骨内容“strict/flexible”修饰符
对于接受 strict/flexible 修饰符的类型(bits、enum 和 union),应始终指定此类修饰符,而不是依赖默认值。也就是说,始终优先选择 flexible bits ... 而不是仅选择 bits ...。
组织机构
语法
- 使用 4 个空格的缩进。
- 切勿使用标签页。
- 避免尾随空格。
- 用一个空行(两个连续的换行符)将
bits、enum、protocol、struct、table和union结构的声明与其他声明分开。 - 在文件末尾插入一个换行符。
评论
注释使用 ///(三个正斜杠)。库中的注释也会显示在生成的代码中,以便在针对库进行编码时简化开发流程。我们称之为评论“流向”目标语言。
将注释放在所描述内容上方。除非属于下列情况,否则请使用合理完整的句子,并注意大小写和句点。将注释宽度限制为 80 个字符,除非不可避免地需要更长的注释(例如,对于长网址)。
评论应以 Markdown 格式撰写。我们的 Markdown 依赖于 CommonMark 规范。有些工具可能会使用其他 Markdown 标准来呈现输出;如果您的工具不使用 CommonMark,我们建议开发者编写与 CommonMark 和其工具都兼容的 Markdown。对 FIDL 元素的引用应始终采用代码字体。
有文档记录的实体是指附有注释的任何 FIDL 元素。注释中对任何已记录实体的首次引用都应使用其完全限定名称,格式为 [`<library>/<top level declaration>.<member>`](例如,[`fuchsia.io/Node.clone`])。如果相应工具支持,此表单可能会生成超链接。后续对该已记录实体的引用可以使用缩写版本,前提是该缩写版本不会产生歧义(例如,clone)。不带方括号的格式不会生成超链接。
描述变量、字段或类型的文档注释的第一部分应是一个名词短语,简要说明所记录实体的预期用途,包括无法从名称和类型推断出的信息。说明应以句点结尾。说明不应重复说明所记录实体的名称或其特定的 FIDL 语言元素类型(例如,struct 或 protocol)。
/// A representation of violins displayed on the screen.
type Widget = struct {
/// A monotonically increasing id, uniquely identifying the widget.
id uint64;
/// Location of the top left corner of the widget.
location Point;
};
以下是一些不应采取的做法:
/// BAD: Widget is a representation of violins displayed on the screen.
/// BAD: struct Widget is a representation of violins displayed on the screen.
附加到协议方法的文档注释的第一部分应该是对该方法行为的简要说明,以动词开头,包括无法从名称和类型推断出的信息。动词应采用现在时态,与第三人称单数代词保持一致,并使用陈述语气(这实际上意味着您应假设动词前面有“it”一词,并且您在陈述事实)。短语应以句点结尾。
请求和响应参数应以内嵌方式记录在相应的结构或表中。
如果某个协议返回错误值,则应在“错误”子部分中记录该值:
## Error
Description of the error value.
完整示例:
/// An abstract representation of a [`fuchsia.io/Node`] whose layout is flat.
protocol File {
compose Node;
/// Acquires a [`fuchsia.mem/Buffer`] representing this file, if
/// there is one, with the requested access rights.
///
/// ## Rights
///
/// This method requires the following rights:
///
/// * [`fuchsia.io/OPEN_RIGHT_WRITABLE`] if `flags` includes
/// [`fuchsia.io/VMO_FLAG_WRITE`].
/// * [`fuchsia.io/OPEN_RIGHT_READABLE`] if `flags` includes
/// [`fuchsia.io/VMO_FLAG_READ`] or [`fuchsia.io/VMO_FLAG_EXEC`].
///
/// ## Error
///
/// Returns ZX_ERR_INVALID_ARGS if `flags` contains an invalid VMO flag.
/// Returns ZX_ERR_NOT_FOUND if the requested buffer does not exist.
///
/// * see [`fuchsia.mem/Buffer`]
/// [`fuchsia.mem/Buffer`]:
/// https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/fidl/fuchsia.mem/buffer.fidl
GetBuffer(struct {
/// A bit field composing any of `VMO_FLAG_READ`, `VMO_FLAG_WRITE`, or
/// `VMO_FLAG_EXEC`.
flags uint32;
}) -> (resource struct {
/// The requested `fuchsia.mem/Buffer`.
buffer box<fuchsia.mem.Buffer>;
}) error zx.Status;
};
由某些外部事实来源定义的类型或值应添加注释,并引用外部来源。例如,引用描述配置结构的 WiFi 规范。同样,如果结构必须与 C 头文件中定义的 ABI 相匹配,请引用该 C 头文件。
如需详细了解注释应包含哪些内容,请参阅 API 文档评分标准。
引用 FIDL 协议或协议方法
注释中对 FIDL 协议或其方法的引用应遵循以下模式:
/// See fuchsia.library/ProtocolName.Method for more information.
如果注释中引用的协议与注释位于同一库中,则可以省略库名称:ProtocolName.Method。
同样,当引用与注释位于同一协议中的方法时,可以省略库名称和协议名称:Method。
库概览
您可以将库概览作为 library 语句的文档注释提供。“library”语句用于启动 FIDL 文件。例如:
/// Library containing example FIDL used throughout the Fuchsia documentation.
library fuchsia.examples.docs;
库概览应提供用于定义库的常规文档。它们还可能会详细介绍各种消息、已定义的协议,以及如何将消息和协议搭配使用。
虽然一个库可以分解为多个 FIDL 文件,但只能有一个库概览。请考虑以下有关库概览的建议:
- 如果概览较短,并且库由单个文件组成,您可以将概览放在库文件顶部的
library语句中。 - 如果库包含多个文件,请创建一个独立的
overview.fidl文件来记录该库。“overview.fidl”文件不应包含任何声明、类型别名或协议定义。
非流经式注释
如果您的注释是针对库作者的,请使用更简单的注释 //(两个正斜杠),这种注释不会传递到目标语言。
在决定哪些内容应作为常规 /// 注释,哪些内容应作为非流经注释时,请注意以下几点。
常规评论:
- 参数、实参、函数的说明
- 使用说明
非流式注释:
- 内部“待办”注释
- 版权通知
- 实现细节
这两种样式的注释可以组合使用:
/// A widget displaying violins on the screen.
// TODO -- widgets should use UUIDs instead of sequential ids
type ViolinWidget = struct {
/// A monotonically increasing id, uniquely identifying the widget.
id uint64;
/// Location of the top left corner of the widget.
location Point;
};
文件
库由一个或多个文件组成。文件存储在具有以下约定的目录层次结构中:
fidl/<library>/[<dir>/]*<file>.fidl
<library> 目录的命名方式是使用以英文句点分隔的 FIDL 库名称。<dir> 子目录是可选的,通常不用于包含文件数量少于 12 个的库。此目录结构与 FIDL 文件在 Fuchsia SDK 中的包含方式相匹配。
将库划分为多个文件对库的使用者没有技术影响。声明(包括协议)可以在整个库中相互引用和自引用,无论它们出现在哪个文件中。将库划分为多个文件,以最大限度提高可读性。
- 首选库中文件的 DAG 依赖关系图。
- 最好将相互引用的定义在文本上彼此靠近,最好位于同一文件中。
- 对于复杂的库,最好在叶文件中定义纯数据类型或常量,并在主干文件中定义引用这些类型的协议。