FIDL 样式指南

本部分包含 Fuchsia 接口定义语言文件的样式相关信息。

另请参阅 FIDL API Rubric

姓名

The Naming of Cats is a difficult matter,
It isn't just one of your holiday games;
--- T.S. Eliot

FIDL 中定义的名称用于生成每个目标语言中的标识符。某些语言会为各种形式的名称附加语义或习惯意义。例如,在 Go 中,标识符中的首字母是否采用大写形式会控制标识符的可见性。因此,许多语言后端都会转换库中的名称,使其更适合目标语言。本部分中的命名规则在 FIDL 源代码的可读性、每个目标语言的可用性以及各个目标语言之间的一致性之间取得了平衡。

避免使用常用的预留字词,例如 goto。语言后端会将保留字转换为非保留标识符,但这些转换会降低这些语言的可用性。避免使用常见的预留字词可减少应用这些转换的频率。

虽然某些 FIDL 关键字在目标语言中也是常用的预留字(例如 C 和 C++ 中的 struct),因此应避免使用,但其他 FIDL 关键字(尤其是 requesthandle)通常具有描述性,可视需要使用。

名称不得包含前导或尾随下划线。在某些语言中,前置或后置下划线具有语义含义(例如,在 Dart 中,前置下划线用于控制可见性),而在其他语言中,前置或后置下划线具有传统含义(例如,在 C++ 中,后置下划线通常用于成员变量)。此外,FIDL 编译器使用前置和后置下划线来混淆标识符,以避免冲突。

使用术语 size 为多个字节命名。使用 count 一词来命名 其他一些数量(例如,矢量中项的数量) 结构体)。

支持请求定义

有时,有多种方法可用于确定如何在标识符中对字词进行分隔。我们的风格如下:

  • 请以美式英语的原始短语(例如,“非 null HTTP 客户端”)
  • 移除所有标点符号。("Non Null HTTP Client")
  • 将所有内容都设为小写(“non null http client”)
  • 根据适用于指定样式的样式,执行以下某项操作 标识符: <ph type="x-smartling-placeholder">
      </ph>
    • 将空格替换为下划线(“_”)以表示小写蛇形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.amberfuchsia.scenic)。当产品在 Fuchsia 之外具有某种外部存在形式,并且协议仅适用于该产品时,产品名称是合适的。例如,fuchsia.cobalt 是 Cobalt 接口协议的名称要优于 fuchsia.metrics,因为 其他指标实现方式(例如Firebase)不太可能实现相同的 协议。

标识符名称应与参与者所扮演的特定角色相关;避免将访问权限控制编码到名称中。基于角色的名称具有描述性,不会像基于访问控制的名称那样很快过时。基于访问控制的名称规定了外部定义的关系,该关系会随着平台的演变而发生变化。例如,对于涉及 FocusChain 对象的 API,适当的名称是 fuchsia.ui.focus,而不是 fuchsia.ui.privileged;如果我们决定让 FocusChain 对象更广泛地提供访问权限,则 fuchsia.ui.focus 就不是问题名称。以下 应避免使用示例字词:

  • constrained
  • limited
  • oem
  • private
  • privileged
  • protected
  • special
  • vendor

标识符名称应有意义;避免使用没有意义的名称。如果 fuchsia.foo.barfuchsia.foo.baz 共享一些您希望提取到单独库中的概念,请考虑在 fuchsia.foo 中定义这些概念,而不是在 fuchsia.foo.common 中定义。应避免使用以下示例字词:

  • common
  • service
  • util
  • base
  • f<letter>l
  • zx<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”修饰符

对于协议,应始终指定 openajarclosed 而不是依赖于默认值也就是说,始终优先使用 open protocol Foo { ...,而不是仅使用 protocol Foo { ...

方法

方法必须是动词短语。

例如,GetBatteryStatusCreateSession 是动词短语,用于指明方法执行的操作。

在事件发生时调用的 listenerobserver 协议的方法 应带有 On 前缀,并描述在 。例如,ViewContainerListener 协议有一个 名为 OnChildAttached

事件

类似地,事件(即从服务器发送至客户端的未经请求的消息) 应带有 On 前缀,并描述过去发生的事件 状态。

例如,AudioCapturer 协议有一个名为 OnPacketCaptured 的事件。

单方法协议

单方法协议的方法应为协议中定义的该方法的动词短语,例如 Loader.LoadGetter.GetUploader.Upload。对于限定名词短语,例如 JobCreatorProcessStopper,则应使用非限定动词短语,例如 JobCreator.CreateProcessStopper.Stop

对于单方法但计划随着时间的推移演变为多方法的协议,不一定需要遵循此命名惯例;也就是说,如果协议有一个已知的扩展,而建议的命名不适合该扩展,则最好尽早选择其他名称。在 因此最好采纳默认建议。

由于替换协议比演进协议更难,因此如果 API 从未打算演进,但最终发现需要改用多方法协议,则最好通过添加方法(可能还需要重命名现有方法)来演进现有协议。

露骨的“strict/flexible”修饰符

对于方法和事件,应始终指定 strictflexible 而不是依赖于默认值也就是说,flexible Foo(); 始终优先于 Foo()

结构体、联合和表

结构体、联合体和表必须是名词短语。例如,Point 是一个用于定义空间中位置的结构体,KeyboardEvent 是一个用于定义与键盘相关的事件的结构体。

结构体、联合和表成员

在以下情况下,最好使用单个单词的结构体、联合体和表成员名称 切合实际(单字词名称在各种目标语言中的呈现方式更加一致)。 不过,如果单个字词会造成歧义或混淆,请不要犹豫使用多个字词。

成员名称不得重复所属类型(或影视库)中的名称,除非 如果成员名称不是封闭类型中的名称,则不明确。对于 例如,包含事件发生时间的 KeyboardEvent 类型的成员 交付项应命名为 time,而不是 event_time,因为名称 event 已出现在封闭类型的名称中。在所有目标中 成员名称的范围取决于所属类型。

不过,用于将智能设备与其所在的房间相关联的类型 DeviceToRoom 可能需要包含成员 device_idroom_name,因为 idname 不明确。这两者都可以指设备或房间。

枚举

枚举必须是名词短语。

例如,PixelFormat 是定义颜色编码方式的枚举 转换为位。

枚举成员

枚举成员名称不得重复所属类型(或库)中的名称。 例如,PixelFormat 枚举的成员应命名为 ARGB,而不是 PIXEL_FORMAT_ARGB,因为名称“PIXEL_FORMAT”已出现在名称中 所属的类型。在所有目标语言中,枚举成员名称的范围取决于其封闭类型。

位字段

位字段必须是名词短语。

例如,InfoFeatures 是一个位字段,用于指示以太网接口上存在哪些功能。

位字段成员

位字段成员不得重复所属类型(或库)中的名称。 例如,InfoFeatures 位字段的成员应命名为 WLAN 而不是 INFO_FEATURES_WLAN,因为名称 INFO_FEATURES 已 是否出现在封闭类型的名称中。 在所有目标语言中,位字段成员名称的范围取决于其封闭类型。

类型

显式“strict/flexible”修饰符

对于接受 strict/flexible 修饰符的类型(bitsenumunion),则应始终指定此类修饰符,而不是依赖于 默认值。也就是说,始终优先选择 flexible bits ...,而不是仅 bits ...

组织机构

语法

  • 使用 4 个空格缩进。
  • 一律不使用标签页。
  • 避免尾随空格。
  • bitsenumprotocolstructtable、 以及包含不同声明的其他声明的 union 构造, 一个空白行(两个连续的换行符)。
  • 文件末尾必须只有一个换行符。

评论

注释使用 ///(三个正斜杠)。库中的评论还会 会显示在生成的代码中,以便在针对 库。将评论“流经”目标语言。

在所描述的内容上方添加注释。除下列情况外,请使用较为完整的句子,并使用适当的大小写和句号。评论长度不得超过 80 个字符,但较长的评论除外 不可避免(例如,对于较长的网址)。

注释应使用 Markdown 编写。我们依靠 CommonMark 规范。某些工具可能会使用其他 Markdown 标准来呈现输出;如果您的工具不使用 CommonMark,我们建议开发者编写与 CommonMark 和其工具兼容的 Markdown。对 FIDL 元素的引用应始终采用代码字体。

已记录的实体是指附带了注释的任何 FIDL 元素。第一个 注释中引用任何已记录的实体时,都应提供其完整 限定名称,格式为 [`<library>/<top level declaration>.<member>`] (例如,[`fuchsia.io/Node.clone`])。在以下情况下,此表单可能会生成超链接: 工具为其提供支持对该已记录实体的后续引用可以使用缩写版本,前提是该缩写版本不含歧义(例如clone)。不带括号的表单不会生成超链接。

描述变量、字段或类型的文档注释的第一部分应为 简要说明记录实体预期用途的名词短语; 包含无法从名称和类型推断出来的信息。通过 说明应以句点结尾。广告内容描述不应 重申记录实体的名称或其特定类型的 FIDL 语言元素(例如,structprotocol)。

/// 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;
};

由某个外部真相来源定义的类型或值应添加注释,并引用外部实体。例如,引用 Wi-Fi 描述配置结构的规范同样,如果结构体必须与 C 头文件中定义的 ABI 相匹配,请引用 C 头文件。

如需详细了解注释应包含的内容,请参阅 API 文档评分标准

引用 FIDL 协议或协议方法

在注释中引用 FIDL 协议或其方法时,应遵循以下模式:

/// See fuchsia.library/ProtocolName.Method for more information.

引用与注释位于同一库中的协议时,可以省略库名称:ProtocolName.Method

同样,在引用与注释相同协议中的方法时,可以省略库名称和协议名称:Method

库概览

您可以将库概览作为 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 依赖关系图。
  • 最好将相互引用的定义放在文本上相近的位置,最好在同一文件中。
  • 对于复杂的库,请优先在叶文件中定义纯数据类型或常量,并在主干文件中定义一起引用这些类型的协议。