FIDL 编译器错误目录

本文档列出了 FIDL 编译器 fidlc 发出的所有错误。此网域中的错误标识符始终以 fi- 前缀开头,后跟一个四位代码,例如 fi-0123。

fi-0001:无效字符

词法分析器未能将指定位置的字符转换为词元。

library test.bad.fi0001;

type ßar = struct {
    value uint64;
};

应通过替换或移除来修正无效字符。

library test.good.fi0001;

type Foo = struct {
    value uint64;
};

无效字符因位置而异。请参阅 FIDL 语言规范,确定 FIDL 语法的每个部分中允许使用哪些字符。

fi-0002:意外换行

字符串字面量不得拆分为多行:

library test.bad.fi0002;

const BAD_STRING string:1 = "Hello
World";

而应使用转义序列 \n 来表示换行符:

library test.good.fi0002;

const GOOD_STRING string:11 = "Hello\nWorld";

fi-0003:转义序列无效

词法分析器在转义序列的开头遇到了无效字符。

library test.bad.fi0003;

const UNESCAPED_BACKSLASH string:2 = "\ ";
const BACKSLASH_TYPO string:1 = "\i";
const CODE_POINT_TYPO string:1 = "\Y1F604";

请替换为有效字符以开始转义序列,或移除意外的反斜杠字符。

library test.good.fi0003;

const ESCAPED_BACKSLASH string:2 = "\\ ";
const REMOVED_BACKSLASH string:1 = "i";
const SMALL_CODE_POINT string:3 = "\u{2604}";
const BIG_CODE_POINT string:4 = "\u{01F604}";

如需了解有效的转义序列,请参阅 FIDL 语法规范。

fi-0004:十六进制数字无效

字符串字面量中的 Unicode 转义不得包含无效的十六进制数字:

library test.bad.fi0004;

const SMILE string = "\u{1G600}";

您必须以十六进制形式指定有效的 Unicode 码位,范围为 0 到 10FFFF。 每个十六进制数字必须是 0 到 9 之间的数字、a 到 f 之间的小写字母或 A 到 F 之间的大写字母。在本例中,G 是 F 的拼写错误:

library test.good.fi0004;

const SMILE string = "\u{1F600}";

fi-0005

fi-0006:预期声明

当 FIDL 预期有声明但发现其他内容时,就会发生此错误。 这通常是由拼写错误导致的。有效声明包括:type、alias、const、using、protocol 和 service。

library test.bad.fi0006;

cosnt SPELLED_CONST_WRONG string:2 = ":("; // Expected a declaraction (such as const).

如需修正此错误,请检查顶级声明中是否存在拼写错误,并确保您仅使用 FIDL 支持的功能。

library test.good.fi0006;

const SPELLED_CONST_RIGHT string:2 = ":)";

fi-0007:非预期的令牌

当解析过程中遇到意外令牌时,就会发生此错误。 一般来说,这是拼写错误造成的:

library test.bad.fi0007;

alias MyType = vector<uint8>:<,256,optional>; // Extra leading comma

此问题的解决方法通常是移除意外标记,或在某些情况下提供缺少的其余语法:

library test.good.fi0007;

alias MyType = vector<uint8>:<256, optional>;

fi-0008:非预期的令牌

每当 FIDL 解析器遇到语法无效的令牌时,就会发生此错误。这可能通过多种方式发生,例如枚举成员缺少 =、多余的标记(如 library = what.is.that.equals.doing.there)等。

library test.bad.unexpectedtokenofkind;

type Numbers = flexible enum {
    ONE; // FIDL enums don't have a default value.
};

通常,此问题的解决方法是添加缺失的令牌或移除多余的令牌。

library test.good.fi0008;

type Numbers = flexible enum {
    ONE = 1;
};

为避免此错误,请检查您的 *.fidl 文件,确保其语法正确。

fi-0009:标识符不符合预期

当标识符在错误的位置使用时,通常会出现此错误:

using test.bad.fi0009;

请改用正确的标识符:

library test.good.fi0009;

fi-0010:标识符无效

发现了一个不符合有效标识符要求的标识符。FIDL 标识符可以包含字母数字和下划线(具体而言,A-Z、a-z、0-9 和 _),此外,每个标识符都必须以字母开头,并以字母或数字结尾。

library test.bad.fi0010a;

// Foo_ is not a valid identifier because it ends with '_'.
type Foo_ = struct {
    value uint64;
};

如需解决此问题,请更改标识符,确保其仅包含有效字符,以字母开头,并以字母或数字结尾。

library test.good.fi0010a;

type Foo = struct {
    value uint64;
};

如果将多部分(带点)标识符传递给属性,也可能会出现此错误。

library test.bad.fi0010b;

@foo(bar.baz="Bar", zork="Zoom")
type Empty = struct{};

如需解决此问题,请改为在属性中仅使用单部分标识符。

library test.good.fi0010b;

@foo(bar="Bar", zork="Zoom")
type Empty = struct {};

fi-0011:库名称组件无效

库名称只能包含字母和数字(A-Z、a-z 和 0-9),并且必须以字母开头。

library test.bad.fi0011.name_with_underscores;

如需解决此问题,请确保所有库名称组件均符合要求。

library test.good.fi0011.namewithoutunderscores;

fi-0012:类型布局类无效

类型声明必须指定 FIDL 已知的布局:

library test.bad.fi00012;

type Foo = invalid {};

有效布局包括 bits、enum、struct、table 和 union:

library test.good.fi0012;

type Empty = struct {};

布局是 FIDL 类型的可参数化说明。它指的是一系列潜在类型,这些类型可以接收更多参数来指定其形状。例如,struct 是一种布局,在定义了特定成员后,它会成为具体类型;而 array 是一种布局,在给定要按顺序重复指定次数的类型后,它会成为具体类型。

布局全部内置于 FIDL 语言中,用户无法指定自己的布局,也无法创建自己的通用类型模板。

fi-0013:封装类型无效

当传递给枚举或位声明的值不是类型的标识符时,就会发生此错误,例如,当您改为提供字符串值作为“后备类型”时:

library test.bad.fi0013;

type TypeDecl = enum : "int32" {
    FOO = 1;
    BAR = 2;
};

如需修正此错误,请确保枚举或位的后备类型是类型标识符。

library test.good.fi0013;

type TypeDecl = enum : int32 {
    FOO = 1;
    BAR = 2;
};

fi-0014:属性包含空括号

如果属性有括号但没有实参,就会发生此错误。

library test.bad.fi0014;

@discoverable()
protocol MyProtocol {};

如需解决此问题,请从没有实参的属性中移除圆括号,或者如果原本打算提供实参,请提供实参。

library test.good.fi0014;

@discoverable
protocol MyProtocol {};

FIDL 不允许在属性中使用空参数列表,这主要是出于样式选择。

fi-0015:属性参数必须全部命名

为清晰起见,当某个属性具有多个参数时,必须明确命名该属性的所有参数。

当某个属性有多个实参,但未明确提供实参名称时,就会发生此错误。

library test.bad.fi0015;

@foo("abc", "def")
type MyStruct = struct {};

如需解决此问题,请使用 name=value 语法为所有实参提供名称。

library test.good.fi0015;

@foo(bar="abc", baz="def")
type MyStruct = struct {};

fi-0016: Missing ordinal before member

当联合或表中的字段缺少序数时,会发生此错误。

library test.bad.fi0016a;

type Foo = table {
    x int64;
};
library test.bad.fi0016b;

type Bar = union {
    foo int64;
    bar vector<uint32>:10;
};

如需修正此错误,请明确指定表或联合的序数:

library test.good.fi0016;

type Foo = table {
    1: x int64;
};

type Bar = union {
    1: foo int64;
    2: bar vector<uint32>:10;
};

与结构体不同,表和联合体旨在允许对其内容进行向后兼容的更改。为了实现这一点,需要一个一致的值(即序数)来标识表字段或联合变体。为避免混淆,并降低在更改表或联合时意外更改序数的可能性,必须始终明确指定序数。

fi-0017:序数超出范围

表和联合的序数必须是有效的无符号 32 位整数。负序数或大于 4,294,967,295 的序数会导致此错误。

library test.bad.fi0017a;

type Foo = table {
  -1: foo string;
};
library test.bad.fi0017b;

type Bar = union {
  -1: foo string;
};

如需修正此错误,请确保所有序数都在允许的范围内。

library test.good.fi0017;

type Foo = table {
    1: foo string;
};

type Bar = union {
    1: foo string;
};

fi-0018:序数必须从 1 开始

table 和 union 成员序数值均不得为 0:

library test.bad.fi0018;

type Foo = strict union {
    0: foo uint32;
    1: bar uint64;
};

而是应从 1 开始编号:

library test.good.fi0018;

type Foo = strict union {
    1: foo uint32;
    2: bar uint64;
};

fi-0019: Strict bits, enum, or union cannot be empty

严格的 bits、enum 或 union 不允许有零个成员:

library test.bad.fi0019;

type Numbers = strict enum {};

而是必须至少有一个成员:

library test.good.fi0019a;

type Numbers = flexible enum {};

或者,您也可以将声明标记为 flexible,而不是 strict:

library test.good.fi0019b;

type Numbers = strict enum {
    ONE = 1;
};

空的 bits、enum 或 union 不包含任何信息,因此通常不应在 API 中使用。不过,灵活的数据类型旨在实现演进,因此定义一个最初为空的灵活位或枚举是有意义的,因为预期以后会添加成员。在定义新的数据类型时,您应始终仔细考虑是使用 strict 还是 flexible。

fi-0020:协议成员无效

如果协议中的某项内容未被识别为有效的协议成员,例如协议中的某项内容不是协议组合、单向方法、双向方法或事件,则会发生此错误。

library test.bad.fi0020;

protocol Example {
    NotAMethodOrCompose;
};

如需修正此错误,请移除无效项,或将其转换为预期协议项类型的正确语法。

library test.good.fi0020;

protocol Example {
    AMethod();
};

fi-0021

fi-0022:无法将属性附加到标识符

如果将属性放置在声明的类型上,而该类型是标识符类型,则会发生此错误。例如,在结构体声明中,将属性放在字段名称之后、字段类型之前,会将属性与字段类型(而非字段本身)相关联。如果字段的类型是按名称引用的预先存在的类型,则无法向其应用其他属性。

library test.bad.fi0022;

type Foo = struct {
    // uint32 is an existing type, extra attributes cannot be added to it just
    // for this field.
    data @foo uint32;
};

如果意图是将属性应用于字段,则应将属性移到字段名称之前。

属性可应用于声明它们的类型。这意味着,如果结构体字段或其他类似声明的类型是匿名类型而非标识符类型,则可以将属性应用于该类型。

library test.good.fi0022;

type Foo = struct {
    // The foo attribute is associated with the data1 field, not the uint32
    // type.
    @foo
    data1 uint32;
    // The type of data2 is a newly declared anonymous structure, so that new
    // type can have an attribute applied to it.
    data2 @foo struct {};
};

fi-0023:类型声明中的属性

借助内嵌布局,您可以将属性直接放在布局前面。但是,在顶层声明类型时,您不能:

library test.bad.fi0023;

type Foo = @foo struct {};

您必须将属性放在 type 关键字之前:

library test.good.fi0023;

@foo
type Foo = struct {};

我们之所以强制执行此规则,是因为如果允许在两个位置都使用属性,会造成混淆。

fi-0024:方法参数列表的文档注释

方法参数列表不能包含文档注释:

library test.bad.fi0024;

protocol Example {
    Method(/// This is a one-way method.
            struct {
        b bool;
    });
};

目前,请将文档注释放在方法本身上:

library test.good.fi0024;

protocol Example {
    /// This is a one-way method.
    Method(struct {
        b bool;
    });
};

此 bug 解决后,此错误将不再出现。该错误是迁移的遗留问题,旨在描述使用 FIDL 类型(而非参数列表)的方法载荷。

fi-0025: 导入组必须位于文件顶部

除了文件顶部的 library 声明之外,文件的 using 导入(如果存在)之前不能有任何其他声明:

library test.bad.fi0025;

alias i16 = int16;
using dependent;

type UsesDependent = struct {
    field dependent.Something;
};

如需解决此错误,请将所有 using 导入内容放在紧跟 library 声明之后的代码块中:

library test.good.fi0025;

using dependent;

alias i16 = int16;
type UsesDependent = struct {
    field dependent.Something;
};

此规则反映了 FIDL 团队的主要审美决定,即依赖项在分组良好且易于定位时更易于阅读。

fi-0026:文档注释块内的注释

注释不应放在文档注释块内:

library test.bad.fi0026;

/// start
// middle
/// end
type Empty = struct {};

而应将注释放在文档注释块之前或之后:

library test.good.fi0026;

// some comments above,
// maybe about the doc comment
/// A
/// multiline
/// comment!
// another comment, maybe about the struct
type Empty = struct {};

通常,紧邻文档注释块之前的注释是放置有关文档注释本身注释的最佳位置。

fi-0027:文档注释块中的空行

文档注释块中不应有空行:

library test.bad.fi0027;

/// start

/// end
type Empty = struct {};

相反,空行只能放在文档注释块之前或之后:

library test.good.fi0027a;

/// A doc comment
type Empty = struct {};

或者,也可以考虑完全省略空行:

library test.good.fi0027b;

/// A doc comment
type Empty = struct {};

fi-0028: Doc comment must be followed by a declaration

文档注释绝不能像常规注释那样自由浮动:

library test.bad.fi0028;

type Empty = struct {};
/// bad

在所有情况下,文档注释都必须直接位于 FIDL 声明之前:

library test.good.fi0028a;

/// A doc comment
type Empty = struct {};

FIDL 会在编译期间将文档注释“降级”为 @doc 属性。事实上,如果需要,任何注释都可以直接以这种方式编写:

library test.good.fi0028b;

@doc("An attribute doc comment")
type Empty = struct {};

从技术角度来看,独立的文档注释是不可编译的,而且在语义上也令人困惑:对“空无一物”进行“文档化”是什么意思?与常规注释不同,文档注释会被处理成结构化文档,因此必须明确它们附加到哪个 FIDL 构造。

fi-0029:资源定义必须至少有一个属性

禁止使用未指定属性的资源定义:

library test.bad.resourcedefinitionnoproperties;

resource_definition SomeResource : uint32 {
  properties {};
};

请至少指定一个属性:

library test.good.fi0029;

resource_definition SomeResource : uint32 {
    properties {
        subtype strict enum : uint32 {
            NONE = 0;
        };
    };
};

这是与 FIDL 内部实现相关的错误,因此只应向开发 FIDL 核心库的开发者显示。最终用户不应看到此错误。

它引用的 resource_definition 声明是 FIDL 定义句柄等资源的内部方法,未来可能会随着句柄泛化工作而发生变化。

fi-0030:修饰符无效

每个 FIDL 修饰符都有一组特定的声明,可在其中使用。不允许在禁止的声明中使用修饰符:

library test.bad.fi0030;

type MyStruct = strict struct {
    i int32;
};

最佳做法是移除违规修饰符:

library test.good.fi0030;

type MyStruct = struct {
    i int32;
};

fi-0031:只有位和枚举可以有子类型

并非每个 FIDL 布局都可以包含子类型:

library test.bad.fi0031;

type Foo = flexible union : uint32 {};

只有 bits 和 enum 布局是在底层类型上定义的。

library test.good.fi0031;

type Foo = flexible enum : uint32 {};

bits 和 enum 布局有些独特,因为它们只是整型 FIDL 基元的受限子类型。因此,它们有必要指定一个作为此子类型的底层类型。相反,struct、table 和 union 布局可以任意大,并且可以包含许多成员,因此全局、布局范围的子类型没有意义。

fi-0032:不允许使用重复的修饰符

禁止在单个声明中指定相同的修饰符:

library test.bad.fi0032;

type MyUnion = strict resource strict union {
    1: foo bool;
};

移除重复的修饰符:

library test.good.fi0032;

type MyUnion = resource strict union {
    1: foo bool;
};

fi-0033: Conflicting modifiers

某些修饰符相互排斥,不能同时修饰同一声明:

library test.bad.conflictingmodifiers;

type StrictFlexibleFoo = strict flexible union {
    1: b bool;
};

type FlexibleStrictBar = flexible strict union {
    1: b bool;
};

一次只能对单个声明使用 strict 或 flexible 修饰符中的一个:

library test.good.fi0033;

type FlexibleFoo = flexible union {
    1: i int32;
};

type StrictBar = strict union {
    1: i int32;
};

目前,只有 strict 和 flexible 修饰符以这种方式互斥。resource 修饰符没有倒数修饰符,因此没有此类限制。

fi-0034:名称冲突

两个声明不能同名:

library test.bad.fi0034;

const COLOR string = "red";
const COLOR string = "blue";

而是为每个声明指定一个唯一的名称:

library test.good.fi0034b;

const COLOR string = "red";
const OTHER_COLOR string = "blue";

或者,如果其中一个声明是误添加的,请将其移除:

library test.good.fi0034a;

const COLOR string = "red";

如需详细了解如何选择名称,请参阅 FIDL 样式指南。

fi-0035:规范名称冲突

两个声明不能具有相同的规范名称:

library test.bad.fi0035;

const COLOR string = "red";

protocol Color {};

尽管 COLOR 和 Color 看起来不同,但它们都由规范名称 color 表示。您可以通过将原始名称转换为 snake_case 来获取规范名称。

如需修正此错误,请为每个声明指定一个在规范化后唯一的名称:

library test.good.fi0035;

const COLOR string = "red";

protocol ColorMixer {};

遵循 FIDL 样式指南的命名准则,可最大限度地降低遇到此错误的可能性。使用相同大小写样式的声明之间绝不会发生规范名称冲突,而使用不同样式的声明之间也很少发生冲突,因为还有其他要求(例如,协议名称通常应为以 -er 结尾的名词短语)。

FIDL 之所以强制执行此规则,是因为绑定生成器会将名称转换为目标语言的惯用命名样式。通过确保规范名称的唯一性,我们保证绑定可以做到这一点,而不会产生名称冲突。如需了解详情,请参阅 RFC-0040:标识符唯一性。

fi-0036:名称重叠

同名声明的可用性不能重叠:

@available(added=1)
library test.bad.fi0036;

type Color = strict enum {
    RED = 1;
};

@available(added=2)
type Color = flexible enum {
    RED = 1;
};

请改用 @available 属性,以确保在任何给定版本中仅存在其中一个声明:

@available(added=1)
library test.good.fi0036;

@available(replaced=2)
type Color = strict enum {
    RED = 1;
};

@available(added=2)
type Color = flexible enum {
    RED = 1;
};

或者,重命名或移除其中一个声明,如 fi-0034 中所示。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0037:规范名称重叠

具有相同规范名称的声明不能具有重叠的可用性:

@available(added=1)
library test.bad.fi0037;

const COLOR string = "red";

@available(added=2)
protocol Color {};

尽管 COLOR 和 Color 看起来不同,但它们都由规范名称 color 表示。您可以通过将原始名称转换为 snake_case 来获取规范名称。

如需修正此错误,请为每个声明指定一个在规范化后唯一的名称。

@available(added=1)
library test.good.fi0037;

const COLOR string = "red";

@available(added=2)
protocol ColorMixer {};

或者,更改其中一个声明的可用性(如 fi-0036 所示),或移除该声明。

如需详细了解 FIDL 为何要求声明具有唯一的规范名称,请参阅 fi-0035。

fi-0038:名称与导入冲突

声明不能与使用 using 导入的库同名:

library dependency;

const VALUE uint32 = 1;
library test.bad.fi0038b;

using dependency;

type dependency = struct {};

// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;

而是应使用 using … as 语法以其他名称导入库:

library test.good.fi0038b;

using dependency as dep;

type dependency = struct {};

const USE_VALUE uint32 = dep.VALUE;

或者,重命名声明以避免冲突:

library test.good.fi0038c;

using dependency;

type OtherName = struct {};

const USE_VALUE uint32 = dependency.VALUE;

您可以通过在库名称中使用多个组成部分来避免此问题。例如,Fuchsia SDK 中的 FIDL 库以 fuchsia. 开头,因此它们至少有两个组件,并且不能与声明名称冲突。

此错误旨在防止歧义。例如,如果 dependency 是一个具有名为 VALUE 的成员的枚举,则 dependency.VALUE 是指该枚举成员还是指在导入的库中声明的常量,就会不明确。

fi-0039:规范名称与导入冲突

声明不能与使用 using 的库导入具有相同的规范名称:

library dependency;

const VALUE uint32 = 1;
library test.bad.fi0039b;

using dependency;

type Dependency = struct {};

// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;

尽管 dependency 和 Dependency 看起来不同,但它们都由规范名称 dependency 表示。您可以通过将原始名称转换为 snake_case 来获取规范名称。

如需修正该错误,请使用 using … as 语法以其他名称导入该库:

library test.good.fi0039b;

using dependency as dep;

type Dependency = struct {};

const USE_VALUE uint32 = dep.VALUE;

或者,重命名声明以避免冲突:

library test.good.fi0039c;

using dependency;

type OtherName = struct {};

const USE_VALUE uint32 = dependency.VALUE;

请参阅 fi-0038,了解此错误存在的原因以及如何避免此错误。

如需详细了解 FIDL 为何要求声明具有唯一的规范名称,请参阅 fi-0035。

fi-0040:文件中的库名称不一致

库可以由多个文件组成,但每个文件都必须具有相同的名称:

library test.bad.fi0040a;
library test.bad.fi0040b;

确保库使用的所有文件都具有相同的名称:

library test.good.fi0040;
library test.good.fi0040;

对于多文件库,建议采用一种惯例,即创建一个空的 overview.fidl 文件,作为库的主要“入口点”。overview.fidl 文件也是放置库级 @available 平台规范的合适位置。

fi-0041:多个库同名

传递给 fidlc 的每个库都必须具有唯一的名称:

library test.bad.fi0041;
library test.bad.fi0041;

确保所有库都具有唯一的名称:

library test.good.fi0041a;
library test.good.fi0041b;

此错误通常是由于向 fidlc 提供的实参方式不正确所致。构成编译所需的每个库(即正在编译的库及其所有传递依赖项)的组成文件必须作为通过 --files 参数传递的单个以空格分隔的文件列表提供,每个库一个此类标志。一个常见错误是尝试在单个 --files 列表中传递所有库的文件。

fi-0042:重复导入库

无法多次导入依赖项:

library test.bad.fi0042a;

type Bar = struct {};
library test.bad.fi0042b;

using test.bad.fi0042a;
using test.bad.fi0042a; // duplicated
type Foo = struct {
    bar test.bad.fi0042a.Bar;
};

确保每个依赖项仅导入一次:

library test.good.fi0042a;

type Bar = struct {};
library test.good.fi0042b;

using test.good.fi0042a;
type Foo = struct {
    bar test.good.fi0042a.Bar;
};

值得注意的是,FIDL 不支持导入同一库的不同版本。@available 版本通过 --available 标志针对整个 fidlc 编译进行解析,这意味着正在编译的库及其所有依赖项在任何给定的编译运行中都必须共享同一版本。

fi-0043:库导入冲突

禁止以与未设置别名的其他导入库名称冲突的方式为导入库设置别名:

library test.bad.fi0043a;

type Bar = struct {};

// This library has a one component name to demonstrate the error.
library fi0043b;

type Baz = struct {};
library test.bad.fi0043c;

using test.bad.fi0043a as fi0043b; // conflict
using fi0043b;

type Foo = struct {
    a fi0043b.Bar;
    b fi0043b.Baz;
};

请选择其他别名以解决名称冲突:

library test.good.fi0043a;

type Bar = struct {};
library fi0043b;

type Baz = struct {};
library test.good.fi0043c;

using test.good.fi0043a as dep;
using fi0043b;

type Foo = struct {
    a dep.Bar;
    b fi0043b.Baz;
};

fi-0044:库导入别名冲突

禁止以与分配给其他导入库的别名冲突的方式为导入库设置别名:

library test.bad.fi0044a;

type Bar = struct {};
library test.bad.fi0044b;

type Baz = struct {};
library test.bad.fi0044c;

using test.bad.fi0044a as dep;
using test.bad.fi0044b as dep; // conflict
type Foo = struct {
    a dep.Bar;
    b dep.Baz;
};

选择不冲突的别名以解决名称冲突:

library test.good.fi0044a;

type Bar = struct {};
library test.good.fi0044b;

type Baz = struct {};
library test.good.fi0044c;

using test.good.fi0044a as dep1;
using test.good.fi0044b as dep2;
type Foo = struct {
    a dep1.Bar;
    b dep2.Baz;
};

fi-0045:使用声明时不允许使用属性

属性无法附加到 using 声明:

library test.bad.fi0045a;

type Bar = struct {};
library test.bad.fi0045b;

/// not allowed
@also_not_allowed
using test.bad.fi0045a;

type Foo = struct {
    bar test.bad.fi0045a.Bar;
};

移除该属性即可解决此错误:

library test.good.fi0045a;

type Bar = struct {};
library test.good.fi0045b;

using test.good.fi0045a;

type Foo = struct {
    bar test.good.fi0045a.Bar;
};

此限制也适用于 /// ... 文档注释,因为这些注释只是 @doc("...") 属性的语法糖。

fi-0046: Unknown library

在大多数情况下,此问题是由于依赖项拼写错误或构建系统未提供依赖项所致。 如果相关依赖项是有意不使用的,则必须移除相关的 using 行:

library test.bad.fi0046;

using dependent; // unknown using.

type Foo = struct {
    dep dependent.Bar;
};

确保使用构建系统将所有导入内容作为依赖项添加到库中。

library test.good.fi0046;

type Foo = struct {
    dep int64;
};

fi-0047

fi-0048:可选表成员

表成员类型不能为 optional:

library test.bad.fi0048;

type Foo = table {
    // Strings can be optional in general, but not in table member position.
    1: t string:optional;
};

从所有成员中移除 optional 限制条件:

library test.good.fi0048;

type Foo = table {
    1: t string;
};

表成员始终是可选的,因此在成员的基础类型上指定此事实是多余的。

表成员始终是可选的,因为在传输过程中,每个表成员都表示为向量中的一个条目。此向量始终表示表中的所有已知字段,因此每个省略的表成员都表示为 null 包络,这与省略的可选类型的表示完全等效。

fi-0049:可选工会成员

联合成员不能是可选的:

library test.bad.fi0049;

type Foo = strict union {
    // Strings can be optional in general, but not in unions.
    1: bar string:optional;
};

移除 optional 限制条件:

library test.good.fi0049;

type Foo = strict union {
    1: bar string;
};

FIDL 不允许 union 成员为可选,因为这可能会导致以多种方式表示同一值。 例如,具有三个可选成员的联合体将有 6 种状态(每个成员 2 种)。相反,应使用类型为 struct {} 的第四个成员对此进行建模,或者使用 Foo:optional 将整个联合体设为可选。

fi-0050:禁止使用已弃用的结构体默认语法

以前,FIDL 允许在 struct 成员上设置默认值:

library test.bad.fi0050;

type MyStruct = struct {
    field int64 = 20;
};

自 RFC-0160:移除对 FIDL 结构体默认值的支持起,此行为已被禁止:

library test.good.fi0050;

type MyStruct = struct {
    field int64;
};

不再允许为 struct 成员设置默认值。用户应改为在应用逻辑中设置此类默认值。

少数使用此语法的旧版用户可以继续使用编译器内置的许可清单,但不会再添加新的例外情况。这些用户迁移完毕后,此功能将从 FIDL 中永久移除。

fi-0051:未知的依赖库

当您使用来自未知库的符号时,会发生此错误。

library test.bad.fi0051;

type Company = table {
    1: employees vector<unknown.dependent.library.Person>;
    2: name string;
};

如需解决此问题,请使用 using 声明导入缺失的依赖库。

library known.dependent.library;

type Person = table {
    1: age uint8;
    2: name string;
};
library test.good.fi0051;
using known.dependent.library;

type Company = table {
    1: employees vector<known.dependent.library.Person>;
    2: name string;
};

当 fidlc 命令行调用格式不正确时,通常会发生此错误。如果您确信未知库存在且应该可解析,请确保通过以空格分隔的列表(传递给 --files 标志)正确传递依赖库的文件。

fi-0052:未找到名称

当您使用的名称无法被 FIDL 编译器找到时,就会发生此错误。

library test.bad.fi0052;

protocol Parser {
    Tokenize() -> (struct {
        tokens vector<string>;
    }) error ParsingError; // ParsingError doesn't exist.
};

如需解决此问题,请移除未找到的名称:

library test.good.fi0052a;

protocol Parser {
    Tokenize() -> (struct {
        tokens vector<string>;
    });
};

或定义未找到的名称:

library test.good.fi0052b;

type ParsingError = flexible enum {
    UNEXPECTED_EOF = 0;
};

protocol Parser {
    Tokenize() -> (struct {
        tokens vector<string>;
    }) error ParsingError;
};

fi-0053: Cannot refer to member

当您引用的成员不是 bits 或 enum 条目时,会发生此错误。

library test.bad.fi0053a;

type Person = struct {
    name string;
    birthday struct {
        year uint16;
        month uint8;
        day uint8;
    };
};

const JOHNS_NAME Person.name = "John Johnson"; // Cannot refer to member of struct 'Person'.
library test.bad.fi0053b;

type Person = struct {
    name string;
    birthday struct {
        year uint16;
        month uint8;
        day uint8;
    };
};

type Cat = struct {
    name string;
    age Person.birthday; // Cannot refer to member of struct 'Person'.
};

如需修正此错误,请更改为具名类型:

library test.good.fi0053a;

type Person = struct {
    name string;
    birthday struct {
        year uint16;
        month uint8;
        day uint8;
    };
};

const JOHNS_NAME string = "John Johnson";

或提取成员的类型:

library test.good.fi0053b;

type Date = struct {
    year uint16;
    month uint8;
    day uint8;
};

type Person = struct {
    name string;
    birthday Date;
};

type Cat = struct {
    name string;
    age Date;
};

fi-0054:位/枚举成员无效

当引用的 enum 或 bits 成员之前未定义时,就会出现此错误。

library test.bad.fi0054;

type Enum = enum {
    foo_bar = 1;
};

const EXAMPLE Enum = Enum.FOO_BAR;

为避免此错误,请确认您之前已为引用的成员值声明了值。这些值区分大小写。

library test.good.fi0054;

type Enum = enum {
    foo_bar = 1;
};

const EXAMPLE Enum = Enum.foo_bar;

fi-0055:对已弃用内容的引用无效

当您使用对 type 或 const 的引用,且该引用具有不兼容的 @available 属性时,会发生此错误。这通常是因为使用了已弃用的 types 或更高版本的 consts。

@available(added=1)
library test.bad.fi0055;

@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;

@available(added=2)
type Color = struct {
    r uint8;
    g uint8;
    b uint8;
    a uint8;
};

@available(added=3)
type Config = table {
    // RGB is deprecated in version 2.
    1: color RGB;
};

如需修正此错误,请使用未弃用的 type 或 const:

@available(added=1)
library test.good.fi0055;

@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;

@available(added=2)
type Color = struct {
    r uint8;
    g uint8;
    b uint8;
    a uint8;
};

@available(added=3)
type Config = table {
    // Using a non-deprecated type.
    1: color Color;
};

fi-0056: Invalid reference to deprecated other platform

当您使用对其他平台中具有不兼容 @available 属性的 type 或 const 的引用时,会发生此错误。这通常是因为从更高版本使用已弃用的 types 或 consts。

@available(platform="foo", added=1)
library test.bad.fi0056a;

@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;

@available(added=2)
type Color = struct {
    r uint8;
    g uint8;
    b uint8;
    a uint8;
};
@available(platform="bar", added=2)
library test.bad.fi0056b;

using test.bad.fi0056a;

@available(added=3)
type Config = table {
    // RGB is deprecated in version 2.
    1: color test.bad.fi0056a.RGB;
};

如需修正此错误,请使用未弃用的 type 或 const:

@available(platform="foo", added=1)
library test.good.fi0056a;

@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;

@available(added=2)
type Color = struct {
    r uint8;
    g uint8;
    b uint8;
    a uint8;
};
@available(platform="bar", added=2)
library test.good.fi0056b;

using test.good.fi0056a;

@available(added=2)
type Config = table {
    // Change to use a non-deprecated type.
    1: color test.good.fi0056a.Color;
};

fi-0057:包含循环

有多种情况可能会导致此问题,但基本上所有情况都归结为 FIDL 声明以无法解析的方式引用自身。这种错误的最简单形式是,类型或协议在其自身定义中直接引用自身:

library test.bad.fi0057c;

type MySelf = struct {
    me MySelf;
};

当类型或协议通过至少一个间接级别传递引用自身时,可能会出现更复杂的失败情况:

library test.bad.fi0057a;

type Yin = struct {
    yang Yang;
};

type Yang = struct {
    yin Yin;
};
library test.bad.fi0057b;

protocol Yin {
    compose Yang;
};

protocol Yang {
    compose Yin;
};

您可以通过在包含周期中的某个位置添加信封(也称为可选性)来解决此错误,因为这样可以在编码/解码时“中断”周期:

library test.good.fi0057;

type MySelf = struct {
    me box<MySelf>;
};
library test.bad.fi0057d;

type MySelf = table {
    1: me MySelf;
};

不允许使用未被信封破坏的递归类型,因为它们无法编码。在上面的第一个示例中,对 MySelf 进行编码需要先对 MySelf 的实例进行编码,而这又需要对 MySelf 的实例进行编码,如此循环往复。此问题的解决方案是通过可选性在此链中添加“中断”,其中可以选择对 MySelf 的另一个嵌套实例进行编码,或者对没有进一步数据的 null 信封进行编码。

fi-0058:引用编译器生成的载荷名称

匿名方法载荷的名称由 FIDL 编译器自动生成,以便生成的后端代码的用户可以根据需要引用它们所代表的类型。不过,禁止在 *.fidl 文件本身中引用这些类型:

library test.bad.fi0058;

protocol MyProtocol {
    strict MyInfallible(struct {
        in uint8;
    }) -> (struct {
        out int8;
    });
    strict MyFallible(struct {
        in uint8;
    }) -> (struct {
        out int8;
    }) error flexible enum {};
    strict -> MyEvent(struct {
        out int8;
    });
};

type MyAnonymousReferences = struct {
    a MyProtocolMyInfallibleRequest;
    b MyProtocolMyInfallibleResponse;
    c MyProtocolMyFallibleRequest;
    d MyProtocol_MyFallible_Result;
    e MyProtocol_MyFallible_Response;
    f MyProtocol_MyFallible_Error;
    g MyProtocolMyEventRequest;
};

如果您希望直接引用载荷类型,则应将载荷类型提取到其自己的命名类型声明中:

library test.good.fi0058;

type MyRequest = struct {
    in uint8;
};
type MyResponse = struct {
    out int8;
};
type MyError = flexible enum {};

protocol MyProtocol {
    strict MyInfallible(MyRequest) -> (MyResponse);
    strict MyFallible(MyRequest) -> (MyResponse) error MyError;
    strict -> MyEvent(MyResponse);
};

type MyAnonymousReferences = struct {
    a MyRequest;
    b MyResponse;
    c MyRequest;
    // There is no way to explicitly name the error result union.
    // d MyProtocol_MyFallible_Result;
    e MyResponse;
    f MyError;
    g MyResponse;
};

所有 FIDL 方法和事件都会为其匿名请求载荷保留 [PROTOCOL_NAME][METHOD_NAME]Request 名称。严格、可靠的双向方法还会预留 [PROTOCOL_NAME][METHOD_NAME]Response。灵活或易出错的双向方法则保留:

  • [PROTOCOL_NAME]_[METHOD_NAME]_Result
  • [PROTOCOL_NAME]_[METHOD_NAME]_Response
  • [PROTOCOL_NAME]_[METHOD_NAME]_Error

由于历史原因,这些名称使用下划线,与其他名称不同。

fi-0059: Invalid constant type

并非所有类型都可以在 const 声明中使用:

library test.bad.fi0059;

const MY_CONST string:optional = "foo";

如果可能,请转换为允许的类型:

library test.good.fi0059;

const MY_CONST string = "foo";

只有 FIDL 基元(bool、int8、int16、int32、int64、uint8、uint16、uint32、uint64、float32、float64)和非可选 string 类型可用于 const 声明的左侧。

fi-0060: Cannot resolve constant value

常量值必须可解析为已知值:

library test.bad.fi0060;

const MY_CONST bool = optional;

确保所使用的常量是有效值:

library test.good.fi0060;

const MY_CONST bool = true;

此错误通常会伴随其他错误,这些错误会提供有关无法解析的预期常量性质的更多信息。

fi-0061:对非基元值使用 OR 运算符

二进制或运算符只能用于基元:

library test.bad.fi0061;

const HI string = "hi";
const THERE string = "there";
const OR_OP string = HI | THERE;

请尝试将要操作的数据表示为 bits 枚举:

library test.good.fi0061;

type MyBits = flexible bits {
    HI = 0x1;
    THERE = 0x10;
};
const OR_OP MyBits = MyBits.HI | MyBits.THERE;

fi-0062:不允许使用 Newtype

RFC-0052:类型别名和新类型中的新类型尚未完全实现,目前还无法使用:

library test.bad.fi0062;

type Matrix = array<float64, 9>;

与此同时,您可以通过定义包含单个元素的结构体来实现类似的效果:

library test.good.fi0062a;

type Matrix = struct {
    elements array<float64, 9>;
};

或者,您也可以定义别名,但请注意,与 newtype 不同,别名不提供类型安全性(即,它可以与其底层类型互换使用):

library test.good.fi0062b;

alias Matrix = array<float64, 9>;

fi-0063: Expected value but got type

const 声明的右侧必须解析为常量值,而不是类型:

library test.bad.fi0063;

type MyType = struct {};
const MY_CONST uint32 = MyType;

确保右侧是一个值:

library test.good.fi0063;

const MY_VALUE uint32 = 8;
const MY_CONST uint32 = MY_VALUE;

fi-0064:位或枚举值类型不正确

在 const 声明中使用 bits 或 enum 变体作为值时,bits/enum 值的类型必须与 const 声明的左侧相同:

library test.bad.fi0064;

type MyEnum = enum : int32 {
    VALUE = 1;
};
type OtherEnum = enum : int32 {
    VALUE = 5;
};
const MY_CONST MyEnum = OtherEnum.VALUE;

一种解决方案是更改 const 声明的类型,使其与所存储值的类型相匹配:

library test.good.fi0064;

type MyEnum = enum : int32 {
    VALUE = 1;
};
type OtherEnum = enum : int32 {
    VALUE = 5;
};
const MY_CONST OtherEnum = OtherEnum.VALUE;

或者,也可以选择其他值来匹配 const 声明的类型:

library test.good.fi0064;

type MyEnum = enum : int32 {
    VALUE = 1;
};
type OtherEnum = enum : int32 {
    VALUE = 5;
};
const MY_CONST MyEnum = MyEnum.VALUE;

fi-0065: Cannot convert value to expected type

常量值的类型必须与其使用位置相符。

导致此错误的最常见原因是 const 声明的值与其声明的类型不匹配:

library test.bad.fi0065a;

const MY_CONST bool = "foo";

如果正确定义的 const 值在底层类型无效的位置使用,仍可能会出现问题:

library test.bad.fi0065b;

const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint8 = ONE | TWO_FIFTY_SIX;

此外,FIDL 的官方会根据架构检查其参数。由于这些实参本身是常量值,因此可能会发生相同类型的类型不匹配:

library test.bad.fi0065c;

protocol MyProtocol {
    @selector(3840912312901827381273)
    MyMethod();
};

在所有这些情况下,解决方案是在接受 const 值的位置仅使用预期类型的值。上述情况分别变为:

library test.good.fi0065a;

const MY_CONST string = "foo";
library test.good.fi0065b;

const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint16 = ONE | TWO_FIFTY_SIX;
library test.good.fi0065c;

protocol MyProtocol {
    @selector("MyOldMethod")
    MyMethod();
};

fi-0066:常量溢出类型

常量值不能超出其底层类型固有的范围:

library test.bad.fi0066;

const NUM uint64 = -42;

您可以通过将值更改为类型范围内的值来解决此问题:

library test.good.fi0066a;

const NUM uint64 = 42;

或者,通过更改为可容纳当前溢出值的类型:

library test.good.fi0066b;

const NUM int64 = -42;

此错误仅涉及 FIDL 的数字类型,所有这些类型都可能溢出。这些范围来自 C++ std::numeric_limits 接口,具体如下:

类型 最小值 最大值
int8 -128 127
int16 32768 32767
int32 2147483648 2147483647
int64 9223372036854775808 9223372036854775807
uint8 0 255
uint16 0 65536
uint32 0 4294967295
uint64 0 18446744073709551615
float32 -3.40282e+38 3.40282e+38
float64 -1.79769e+308 1.79769e+308

fi-0067:Bits 成员必须是 2 的幂

bits 声明中所有成员的值都不得是任何非 2 的幂的数字:

library test.bad.fi0067;

type NonPowerOfTwo = bits : uint64 {
    THREE = 3;
};

相反,成员值应始终为 2 的幂:

library test.good.fi0067a;

type Fruit = bits : uint64 {
    ORANGE = 1;
    APPLE = 2;
    BANANA = 4;
};

要避免受此限制影响,一个简单的方法是仅对位成员值使用位掩码,而不是十进制数:

library test.good.fi0067b;

type Life = bits {
    A = 0b000010;
    B = 0b001000;
    C = 0b100000;
};

bits 构造表示一个位数组。这是表示布尔标志序列的最节省内存的方式。由于 bits 声明的每个成员都映射到其底层内存的特定位,因此用于该映射的值必须明确标识要分配给无符号整数的特定位。

fi-0068:灵活枚举具有预留的未知值

当您定义的枚举成员的值与预留的未知值冲突时,就会发生此错误。

灵活枚举可能包含 FIDL 架构未知的值。此外,灵活枚举始终会保留一些将被视为未知的值。默认情况下,该值是该枚举的基础整数类型可表示的最大数值(例如,对于 uint8,该值为 255)。

library test.bad.fi0068;

type Foo = flexible enum : uint8 {
    ZERO = 0;
    ONE = 1;
    MAX = 255;
};

如需修正此错误,您可以移除该成员或更改其值:

library test.good.fi0068a;

type Foo = flexible enum : uint8 {
    ZERO = 0;
    ONE = 1;
};
library test.good.fi0068b;

type Foo = flexible enum : uint8 {
    ZERO = 0;
    ONE = 1;
    MAX = 254;
};

最后,如果您在将 strict 枚举转换为 flexible 枚举时遇到此错误,可以使用 @unknown 属性将特定成员的数值指定为未知值。请参阅 @unknown。

fi-0069:位必须使用无符号整型子类型

禁止使用有符号数字作为 bits 声明的基础类型:

library test.bad.fi0069;

type Fruit = bits : int64 {
    ORANGE = 1;
    APPLE = 2;
    BANANA = 4;
};

而应使用以下任一选项:uint8、uint16、uint32 或 uint64:

library test.good.fi0069;

type Fruit = bits : uint64 {
    ORANGE = 1;
    APPLE = 2;
    BANANA = 4;
};

与 enum 声明(允许有符号和无符号整数,请参阅:fi-0070)不同,bits 声明仅允许无符号整数。这是因为每个 bits 成员都必须表示位数组中的特定底层位(这就是 fi-0067 存在的原因)。最简洁的表示方式是使用单个无符号整数。无符号整数的二进制表示法直接映射到单个位(2 的索引次方),而有符号整数中的负数由于二进制补码表示法的机制,几乎总是选择多个位。

fi-0070:枚举必须使用整型子类型

禁止使用非整数数值 float32 或 float64 作为 enum 声明的基础类型:

library test.bad.fi0070;

type MyEnum = enum : float64 {
    ONE_POINT_FIVE = 1.5;
};

请改用以下任一选项:int8、int16、int32、int64、uint8、uint16、uint32 或 uint64:

library test.good.fi0070;

type MyEnum = enum : uint64 {
    ONE = 1;
};

fi-0071:严格枚举成员上不允许使用未知属性

strict enum 不得有任何成员使用 @unknown 属性进行注解:

library test.bad.fi0071;

type MyEnum = strict enum : int8 {
    @unknown
    UNKNOWN = 0;
    FOO = 1;
    MAX = 127;
};

如需继续使用 @unknown 属性,请更改为 flexible enum:

library test.good.fi0071a;

type MyEnum = flexible enum : int8 {
    @unknown
    UNKNOWN = 0;
    FOO = 1;
    MAX = 127;
};

否则,只需完全移除该属性即可保持为 strict enum:

library test.good.fi0071;

type MyEnum = strict enum : int8 {
    UNKNOWN = 0;
    FOO = 1;
    MAX = 127;
};

@unknown 属性的用途是顺利完成从具有用户定义的未知值的 strict enum(如下所示)到具有 FIDL 已知并处理的未知值的 flexible enum 的转换。在上面的示例中,它将用于从第二种正确用法过渡到第一种正确用法。

fi-0072:只有枚举成员可以带有未知属性

禁止使用 @unknown 属性修饰多个 enum 成员:

library test.bad.fi0072;

type MyEnum = flexible enum : uint8 {
    @unknown
    UNKNOWN = 0;
    @unknown
    OTHER = 1;
};

仅选择并注解用作网域特定“未知”值的成员:

library test.good.fi0071a;

type MyEnum = flexible enum : int8 {
    @unknown
    UNKNOWN = 0;
    OTHER = 1;
};

@unknown 属性的用途是,顺利完成从具有用户定义的未知值的 strict enum(如下所示)到具有 FIDL 已知并处理的未知值的 flexible enum 的转换:

library test.good.fi0072;

type MyEnum = strict enum : int8 {
    UNKNOWN = 0;
    OTHER = 1;
};
library test.good.fi0071a;

type MyEnum = flexible enum : int8 {
    @unknown
    UNKNOWN = 0;
    OTHER = 1;
};

fi-0073:撰写非协议

compose 语句中只能使用协议:

library test.bad.fi0073;

type MyStruct = struct {};

protocol MyProtocol {
    compose MyStruct;
};

请确保您引用的名称指向某个协议:

library test.good.fi0073;

protocol MyOtherProtocol {};

protocol MyProtocol {
    compose MyOtherProtocol;
};

fi-0074:方法载荷使用了无效的布局

只有 struct、table 或 union 布局可用于描述方法载荷:

library test.bad.fi0074;

protocol MyProtocol {
    MyMethod(enum {
        FOO = 1;
    });
};

请改用以下布局之一:

library test.good.fi0074;

protocol MyProtocol {
    MyMethod(struct {
        foo bool;
    });
};

fi-0075: Invalid primitive used for method payload

基元不能用作方法载荷:

library test.bad.fi0075;

protocol MyProtocol {
    MyMethod(uint32);
};

请改用 struct、table 或 union 布局类型:

library test.good.fi0075;

protocol MyProtocol {
    MyMethod(struct {
        wrapped_in_struct uint32;
    });
};

如果所需的载荷实际上只是一个基元值,并且无需考虑未来的演变,那么将该值封装在 struct 布局中会产生一个与所需值本身大小相同的载荷。

fi-0076

fi-0077: Interaction payload cannot be empty struct

方法或事件中的载荷不能是空结构体:

library test.bad.fi0077a;

protocol Test {
    MyMethod(struct {}) -> (struct {});
};
library test.bad.fi0077b;

protocol Test {
    -> MyEvent(struct {});
};

如果您想表示特定请求/响应不包含任何信息,请删除空结构体,并在该位置保留 ():

library test.good.fi0077a;

protocol Test {
    MyMethod() -> ();
};
library test.good.fi0077b;

protocol Test {
    -> MyEvent();
};

空结构体无法扩展,并且在传输时占用 1 个字节。由于 FIDL 支持不带载荷的交互,因此以这种方式使用空结构体是多余的,效率也较低。因此,此类应用不予上架。

fi-0078

fi-0079

fi-0080: Generated zero value ordinal

此错误不应发生。如果你成功做到了这一点,恭喜你,你可能已经破解了 SHA-256!

玩笑归玩笑,如果 fidlc 编译器生成序数值 0,就会发生此错误。这种情况绝不该发生,因此如果确实发生了,您可能发现了 FIDL 编译器中的 bug。如果发生这种情况,请向我们的问题跟踪器报告问题。

fi-0081: Duplicate method ordinal

当您使用 @selector 属性使两个方法名称产生相同的序数时,通常会出现此错误。

library test.bad.fi0081;

protocol Parser {
    ParseLine();

    // Multiple methods with the same ordinal...
    @selector("ParseLine")
    ParseOneLine();
};

如需解决此问题,请更新方法名称或选择器,使其不发生冲突。

library test.good.fi0081;

protocol Parser {
    ParseLine();

    @selector("Parse1Line")
    ParseOneLine();
};

如果发生 SHA-256 冲突,也可能会出现此错误,但发生这种情况的几率基本上为零。如果您确信选择器没有问题,但仍然遇到此错误,则可能是发现了 FIDL 编译器中的 bug。 如果发生这种情况,请向我们的问题跟踪器报告问题。

fi-0082:选择器值无效

当您为 @selector 使用无效值时,会发生此错误。 最常见的原因是拼写错误。选择器必须是独立的方法名称或完全限定的方法名称。

library test.bad.fi0082;

protocol Parser {
    @selector("test.old.fi0082.Parser.Parse")
    Parse();
};

如需解决此问题,请将选择器更新为有效的独立方法名称或完全限定的方法名称:

library test.good.fi0082;

protocol Parser {
    @selector("test.old.fi0082/Parser.Parse")
    Parse();
};

fi-0083:fuchsia.io 必须使用显式序数

FIDL 编译器过去会自动将 fuchsia.io 序数重命名为 fuchsia.io1。这种神奇机制旨在让方法的 io2 版本具有“正常”序数,从而更轻松地迁移到 fuchsia.io2。不过,这个系统最终有点过于神奇,因此现在需要手动为 fuchsia.io 提供序数。

library fuchsia.io;

protocol SomeProtocol {
    SomeMethod();
};

如需解决此问题,请手动提供一个选择器,使用 fuchsia.io1 作为库名称,以便将 fuchsia.io 名称用于 io2。

library fuchsia.io;

protocol SomeProtocol {
    @selector("fuchsia.io1/SomeProtocol.SomeMethod")
    SomeMethod();
};

fi-0084:方法载荷结构体中不允许使用默认成员

用作方法载荷的结构体不得指定默认成员:

library test.bad.fi0084;

type MyStruct = struct {
    @allow_deprecated_struct_defaults
    a bool = false;
};

protocol MyProtocol {
    MyMethod(MyStruct) -> (MyStruct);
};

从相关 struct 声明中移除默认成员:

library test.good.fi0084;

type MyStruct = struct {
    a bool;
};

protocol MyProtocol {
    MyMethod(MyStruct) -> (MyStruct);
};

fi-0085

fi-0086

fi-0087

fi-0088:服务成员不能是可选的

当您将服务成员标记为 optional 时,会发生此错误。不允许将服务成员标记为 optional,因为服务成员始终是可选的。

library test.bad.fi0088;

protocol Sorter {
    Sort(struct {
        input vector<int32>;
    }) -> (struct {
        output vector<int32>;
    });
};

service SortService {
    quicksort client_end:<Sorter, optional>;
    mergesort client_end:<Sorter, optional>;
};

如需解决此问题,请移除可选子句:

library test.good.fi0088;

protocol Sorter {
    Sort(struct {
        input vector<int32>;
    }) -> (struct {
        output vector<int32>;
    });
};

service SortService {
    quicksort client_end:Sorter;
    mergesort client_end:Sorter;
};

fi-0089

fi-0090

fi-0091:结构体成员类型无效

当您尝试为不受支持的类型设置默认结构体值时,会发生此错误。只有数字和布尔值类型可以设置默认结构体值。

library test.bad.fi0091;

type Person = struct {
    @allow_deprecated_struct_defaults
    name string:optional = "";
};

如需解决此问题,请移除默认值:

library test.good.fi0091;

type Person = struct {
    @allow_deprecated_struct_defaults
    name string:optional;
};

fi-0092: Table ordinal too large

FIDL 表序数不能大于 64:

library test.bad.fi0092;

type Table64thField = table {
    1: x int64;
};

type Example = table {
    1: v1 int64;
    2: v2 int64;
    3: v3 int64;
    4: v4 int64;
    5: v5 int64;
    6: v6 int64;
    7: v7 int64;
    8: v8 int64;
    9: v9 int64;
    10: v10 int64;
    11: v11 int64;
    12: v12 int64;
    13: v13 int64;
    14: v14 int64;
    15: v15 int64;
    16: v16 int64;
    17: v17 int64;
    18: v18 int64;
    19: v19 int64;
    20: v20 int64;
    21: v21 int64;
    22: v22 int64;
    23: v23 int64;
    24: v24 int64;
    25: v25 int64;
    26: v26 int64;
    27: v27 int64;
    28: v28 int64;
    29: v29 int64;
    30: v30 int64;
    31: v31 int64;
    32: v32 int64;
    33: v33 int64;
    34: v34 int64;
    35: v35 int64;
    36: v36 int64;
    37: v37 int64;
    38: v38 int64;
    39: v39 int64;
    40: v40 int64;
    41: v41 int64;
    42: v42 int64;
    43: v43 int64;
    44: v44 int64;
    45: v45 int64;
    46: v46 int64;
    47: v47 int64;
    48: v48 int64;
    49: v49 int64;
    50: v50 int64;
    51: v51 int64;
    52: v52 int64;
    53: v53 int64;
    54: v54 int64;
    55: v55 int64;
    56: v56 int64;
    57: v57 int64;
    58: v58 int64;
    59: v59 int64;
    60: v60 int64;
    61: v61 int64;
    62: v62 int64;
    63: v63 int64;
    // The 64th field of a table must be another table, otherwise it will cause
    // fi-0093: Max Ordinal In Table Must Be Table.
    64: v64 Table64thField;
    65: v65 int64;
};

为了允许增长超过 64 个序数,FIDL 要求表的最后一个字段是另一个表。任何超过 64 个的表字段都必须放在嵌套表中。

library test.good.fi0092;

type Table64thField = table {
    1: x int64;
    // Any fields beyond 64 of table Example must be move to the nested table in
    // ordinal 64 of Example.
    2: v65 int64;
};

type Example = table {
    1: v1 int64;
    2: v2 int64;
    3: v3 int64;
    4: v4 int64;
    5: v5 int64;
    6: v6 int64;
    7: v7 int64;
    8: v8 int64;
    9: v9 int64;
    10: v10 int64;
    11: v11 int64;
    12: v12 int64;
    13: v13 int64;
    14: v14 int64;
    15: v15 int64;
    16: v16 int64;
    17: v17 int64;
    18: v18 int64;
    19: v19 int64;
    20: v20 int64;
    21: v21 int64;
    22: v22 int64;
    23: v23 int64;
    24: v24 int64;
    25: v25 int64;
    26: v26 int64;
    27: v27 int64;
    28: v28 int64;
    29: v29 int64;
    30: v30 int64;
    31: v31 int64;
    32: v32 int64;
    33: v33 int64;
    34: v34 int64;
    35: v35 int64;
    36: v36 int64;
    37: v37 int64;
    38: v38 int64;
    39: v39 int64;
    40: v40 int64;
    41: v41 int64;
    42: v42 int64;
    43: v43 int64;
    44: v44 int64;
    45: v45 int64;
    46: v46 int64;
    47: v47 int64;
    48: v48 int64;
    49: v49 int64;
    50: v50 int64;
    51: v51 int64;
    52: v52 int64;
    53: v53 int64;
    54: v54 int64;
    55: v55 int64;
    56: v56 int64;
    57: v57 int64;
    58: v58 int64;
    59: v59 int64;
    60: v60 int64;
    61: v61 int64;
    62: v62 int64;
    63: v63 int64;
    64: v64 Table64thField;
};

表中的每个字段都会产生 FIDL 信封的开销,以便该字段成为可选字段。这样,表的每个字段都可以存在或不存在,并且可以通过添加或移除字段来演进表,但代价是内存开销比结构体大得多。

一般来说,您可以通过避免在表中包含细粒度的小字段来避免此错误并减少开销。相反,您可以将预计需要同时添加或移除的元素分组到结构体中,并将这些结构体用作表的字段。这样可以减少开销,并避免序号用尽,但代价是可演进性会有所降低。

在 RFC-0132:FIDL 表大小限制中,这变成了一个错误,旨在防止用户意外产生非常大的表的开销。这种额外开销在架构中并不明显,尤其是在只有少数几个字段(具有较大的序数)或有许多字段但一次只使用几个字段的情况下。

fi-0093: Max ordinal in table must be table

FIDL 表中第 64 个成员的类型本身必须是表:

library test.bad.fi0093;

type Example = table {
    1: v1 int64;
    2: v2 int64;
    3: v3 int64;
    4: v4 int64;
    5: v5 int64;
    6: v6 int64;
    7: v7 int64;
    8: v8 int64;
    9: v9 int64;
    10: v10 int64;
    11: v11 int64;
    12: v12 int64;
    13: v13 int64;
    14: v14 int64;
    15: v15 int64;
    16: v16 int64;
    17: v17 int64;
    18: v18 int64;
    19: v19 int64;
    20: v20 int64;
    21: v21 int64;
    22: v22 int64;
    23: v23 int64;
    24: v24 int64;
    25: v25 int64;
    26: v26 int64;
    27: v27 int64;
    28: v28 int64;
    29: v29 int64;
    30: v30 int64;
    31: v31 int64;
    32: v32 int64;
    33: v33 int64;
    34: v34 int64;
    35: v35 int64;
    36: v36 int64;
    37: v37 int64;
    38: v38 int64;
    39: v39 int64;
    40: v40 int64;
    41: v41 int64;
    42: v42 int64;
    43: v43 int64;
    44: v44 int64;
    45: v45 int64;
    46: v46 int64;
    47: v47 int64;
    48: v48 int64;
    49: v49 int64;
    50: v50 int64;
    51: v51 int64;
    52: v52 int64;
    53: v53 int64;
    54: v54 int64;
    55: v55 int64;
    56: v56 int64;
    57: v57 int64;
    58: v58 int64;
    59: v59 int64;
    60: v60 int64;
    61: v61 int64;
    62: v62 int64;
    63: v63 int64;
    64: v64 int64;
};

如果用户发现自己需要添加第 64 个成员,则应创建一个单独的表来保存第 64 个及后续成员,并将该成员放入该表中:

library test.good.fi0093;

type Table64thField = table {
    1: x int64;
};

type Example = table {
    1: v1 int64;
    2: v2 int64;
    3: v3 int64;
    4: v4 int64;
    5: v5 int64;
    6: v6 int64;
    7: v7 int64;
    8: v8 int64;
    9: v9 int64;
    10: v10 int64;
    11: v11 int64;
    12: v12 int64;
    13: v13 int64;
    14: v14 int64;
    15: v15 int64;
    16: v16 int64;
    17: v17 int64;
    18: v18 int64;
    19: v19 int64;
    20: v20 int64;
    21: v21 int64;
    22: v22 int64;
    23: v23 int64;
    24: v24 int64;
    25: v25 int64;
    26: v26 int64;
    27: v27 int64;
    28: v28 int64;
    29: v29 int64;
    30: v30 int64;
    31: v31 int64;
    32: v32 int64;
    33: v33 int64;
    34: v34 int64;
    35: v35 int64;
    36: v36 int64;
    37: v37 int64;
    38: v38 int64;
    39: v39 int64;
    40: v40 int64;
    41: v41 int64;
    42: v42 int64;
    43: v43 int64;
    44: v44 int64;
    45: v45 int64;
    46: v46 int64;
    47: v47 int64;
    48: v48 int64;
    49: v49 int64;
    50: v50 int64;
    51: v51 int64;
    52: v52 int64;
    53: v53 int64;
    54: v54 int64;
    55: v55 int64;
    56: v56 int64;
    57: v57 int64;
    58: v58 int64;
    59: v59 int64;
    60: v60 int64;
    61: v61 int64;
    62: v62 int64;
    63: v63 int64;
    64: v64 Table64thField;
};

RFC-0132:FIDL 表大小限制中详细阐述了此要求背后的原因和动机。简而言之,FIDL 表需要对允许的字段数量进行相对严格的限制,否则,一次仅使用几个字段的表的编码形式将具有不可接受的大量死空间(每个省略的成员 16 字节)。

为了给希望在表中添加超过 64 个字段的用户提供解决方法,FIDL 会强制将最后一个序数保留给包含额外字段的“延续表”。在此位置使用任何其他类型都会导致该表在未来无法扩展。

fi-0094:表成员序数重复

table 声明中用于成员的序数不能重复:

library test.bad.fi0094;

type MyTable = table {
    1: my_field string;
    1: my_other_field uint32;
};

根据需要递增序数,以确保声明的所有成员都具有唯一的序数:

library test.good.fi0094a;

type MyTable = table {
    1: my_field string;
    2: my_other_field uint32;
};

或者,也可以移除名称重复的成员之一:

library test.good.fi0094b;

type MyTable = table {
    1: my_field string;
};

序数用于标识网络上的字段。如果两个成员共用一个序数,则在解码 FIDL 消息时,无法可靠地判断引用的是哪个字段。

fi-0095

fi-0096

fi-0097:联合成员序数重复

union 声明中用于成员的序数不能重复:

library test.bad.fi0097;

type MyUnion = strict union {
    1: my_variant string;
    1: my_other_variant int32;
};

根据需要递增序数,以确保声明的所有成员都具有唯一的序数:

library test.good.fi0097a;

type MyUnion = strict union {
    1: my_variant string;
    2: my_other_variant int32;
};

或者,也可以移除名称重复的成员之一:

library test.good.fi0097b;

type MyUnion = strict union {
    1: my_variant string;
};

序数用于标识传输中的变体。如果两个成员共用一个序数,则在解码 FIDL 消息时,无法可靠地判断引用的是哪个变体。

fi-0098

fi-0099

fi-0100

fi-0101:无法解析的大小限制

应用于 vector 或 string 类型定义的大小限制条件必须是 uint32 类型的有效值:

library test.bad.fi0101a;

alias MyBoundedOptionalVector = vector<uint32>:<"255", optional>;
library test.bad.fi0101b;

alias MyBoundedOptionalVector = vector<uint32>:<uint8, optional>;

请确保满足以下条件:

library test.good.fi0101;

alias MyBoundedOptionalVector = vector<uint32>:<255, optional>;

fi-0102:无法解析的成员值

bits 和 enum 声明的成员必须是指定子类型的可解析值:

library test.bad.fi0102;

type Fruit = bits : uint64 {
    ORANGE = 1;
    APPLE = 2;
    BANANA = -4;
};

确保所有值都与声明的基础类型匹配:

library test.good.fi0102;

type Fruit = bits : uint64 {
    ORANGE = 1;
    APPLE = 2;
    BANANA = 4;
};

fi-0103:无法解析结构体默认值

struct 声明的成员的默认值必须与其各自成员的声明类型相匹配:

library test.bad.fi0103;

type MyEnum = enum : int32 {
    A = 1;
};

type MyStruct = struct {
    @allow_deprecated_struct_defaults
    field MyEnum = 1;
};

确保值与声明的类型匹配:

library test.good.fi0103;

type MyEnum = enum : int32 {
    A = 1;
};

type MyStruct = struct {
    @allow_deprecated_struct_defaults
    field MyEnum = MyEnum.A;
};

fi-0104:无法解析的属性参数

根据属性架构对该实参的预期,官方 FIDL 属性的实参值不能无效:

library test.bad.fi0104;

type MyStruct = struct {
    my_field @generated_name(true) struct {};
};

确保用作属性实参的值类型正确:

library test.good.fi0104;

type MyStruct = struct {
    my_field @generated_name("my_inner_type") struct {};
};

fi-0105

fi-0106

fi-0107:成员值重复

bits 和 enum 声明都不能包含具有相同值的成员:

library test.bad.fi0107;

type Fruit = flexible enum {
    ORANGE = 1;
    APPLE = 1;
};

将成员值更改为全部唯一:

library test.good.fi0107a;

type Fruit = flexible enum {
    ORANGE = 1;
    APPLE = 2;
};

或者,移除其中一个重复的成员:

library test.good.fi0107b;

type Fruit = flexible enum {
    ORANGE = 1;
};

fi-0108

fi-0109

fi-0110: 包含类型的资源必须标记为资源

如果某个类型包含句柄(无论是直接包含,还是通过传递包含另一个包含句柄的类型),则必须将该类型指定为 resource,否则无法声明该类型:

library test.bad.fi0110;

using zx;

type Foo = struct {
    handle zx.Handle;
};

有两种可能的解决方案。第一种方法是使用资源修饰符为有问题的声明添加注解:

library test.good.fi0110a;

using zx;

type Foo = resource struct {
    handle zx.Handle;
};

或者,也可以选择完全移除包含 resource 的类型,从而无需在所属声明中使用修饰符:

library test.good.fi0110b;

type Foo = struct {
    value uint32;
};

如需了解添加 resource 修饰符的原因和动机,以及此错误强制执行的使用模式的“传染性”性质,请参阅 RFC-0057:默认无句柄。

fi-0111:内嵌大小超出限制

不允许使用内嵌大小为 64 KiB 或更大的 FIDL 类型:

library test.bad.fi0111;

type MyStruct = struct {
    numbers array<uint8, 65536>;
};

请确保类型的内嵌大小小于 64 KiB。在这种情况下,我们可以调整数组边界:

library test.good.fi0111;

type MyStruct = struct {
    numbers array<uint8, 65535>;
};

此限制是出于性能原因而设置的。这意味着编码器和解码器可以假定大小和偏移量适合无符号 16 位整数。

除非您使用大型数组或深度嵌套的结构体,否则在实践中不太可能遇到这种情况。大多数 FIDL 构造(例如字符串、向量、表和联合)都使用非内嵌存储,这不会计入其各自的内嵌大小。

fi-0112:服务成员不是 client_end

服务成员只能是客户端,不能是任何其他类型:

library test.bad.fi0112;

protocol Calculator {};

service Service {
    calculator server_end:Calculator;
};

如需修正此错误,请确保成员具有某种协议 P 的形式 client_end:P:

library test.good.fi0112;

protocol Calculator {};

service Service {
    calculator client_end:Calculator;
};

服务是协议实例的集合,而不是通用数据结构,因此允许任意类型是没有意义的。

fi-0113:服务中的传输不匹配

FIDL 服务不得包含使用不同传输协议的协议:

library test.bad.fi0113;

protocol ChannelProtocol {};

@transport("Driver")
protocol DriverProtocol {};

service SomeService {
    a client_end:ChannelProtocol;
    b client_end:DriverProtocol;
};

而是应为每个传输使用单独的服务:

library test.good.fi0113;

protocol ChannelProtocol {};

@transport("Driver")
protocol DriverProtocol {};

service ChannelService {
    protocol client_end:ChannelProtocol;
};

service DriverService {
    protocol client_end:DriverProtocol;
};

请注意,服务是 FIDL 中尚未完成的功能。它们最初是在 RFC-0041:支持统一服务和设备中设计的。如需了解截至 2022 年 10 月的状态,请参阅 https://fxbug.dev/42160684。

fi-0114:组合协议过于开放

协议不能组合比自身更开放的其他协议:

library test.bad.fi0114;

open protocol Composed {};

ajar protocol Composing {
    compose Composed;
};

您可以通过提高组合协议的开放性来解决此问题,即将其从 closed 更改为 ajar,或从 ajar 更改为 open:

library test.good.fi0114a;

open protocol Composed {};

open protocol Composing {
    compose Composed;
};

或者,您也可以降低组合协议的开放性,即将其从 open 更改为 ajar,或从 ajar 更改为 closed:

library test.good.fi0114b;

ajar protocol Composed {};

ajar protocol Composing {
    compose Composed;
};

之所以存在此规则,是因为协议的开放性限制了其允许包含的方法类型。例如,半开协议不能包含灵活的双向方法,但开放协议可以,因此半开协议组合开放协议是不安全的。

如需详细了解协议修饰符,请参阅 RFC-0138:处理未知互动。

fi-0115:灵活的双向方法需要开放协议

封闭式和半开放式协议不得包含灵活的双向方法:

library test.bad.fi0115;

ajar protocol Protocol {
    flexible Method() -> ();
};

而是应标记双向方法 strict,而不是 flexible:

library test.good.fi0115a;

ajar protocol Protocol {
    strict Method() -> ();
};

或者,将协议标记为 open,而不是 closed 或 ajar:

library test.good.fi0115b;

open protocol Protocol {
    flexible Method() -> ();
};

之所以会出现此错误,是因为 closed(或 ajar)修饰符的作用是确保方法不包含任何灵活(双向)方法。首次创建协议时,您应根据协议所需的可演变性属性,仔细考虑协议应为封闭式、半开放式还是开放式。

如需详细了解协议修饰符,请参阅 RFC-0138:处理未知互动。

fi-0116:灵活的单向方法需要使用 ajar 或 open 协议

封闭式协议不得包含灵活的单向方法:

library test.bad.fi0116;

closed protocol Protocol {
    flexible Method();
};

而是将单向方法标记为 strict,而不是 flexible:

library test.good.fi0116;

closed protocol Protocol {
    strict Method();
};

或者,将协议标记为 ajar 或 open,而不是 closed:

library test.good.fi0116;

ajar protocol Protocol {
    flexible Method();
};

之所以会出现此错误,是因为 closed 修饰符的作用是确保方法不包含任何灵活方法。首次创建协议时,您应根据协议所需的可演化性属性,仔细考虑协议应为封闭、半开放还是开放。

如需详细了解协议修饰符,请参阅 RFC-0138:处理未知互动。

fi-0117:在不兼容的传输中使用了句柄

协议只能引用与其传输兼容的句柄。 例如,通过 Zircon 通道传输的协议无法引用 Fuchsia 驱动程序框架句柄:

library test.bad.fi0117;

using fdf;

protocol Protocol {
    Method(resource struct {
        h fdf.handle;
    });
};

而是使用与协议传输兼容的句柄:

library test.good.fi0117a;

using zx;

protocol Protocol {
    Method(resource struct {
        h zx.Handle;
    });
};

或者,更改协议的传输方式以匹配句柄:

library test.good.fi0117b;

using fdf;

@transport("Driver")
protocol Protocol {
    Method(resource struct {
        h fdf.handle;
    });
};

fi-0118:在不兼容的传输中使用了传输端

协议只能引用通过同一传输协议传输的协议的传输端(client_end 和 server_end)。例如,使用 Syscall 传输的协议无法引用使用 Driver 传输的协议的客户端端点:

library test.bad.fi0118;

@transport("Driver")
protocol DriverProtocol {};

@transport("Syscall")
protocol P {
    M(resource struct {
        s client_end:DriverProtocol;
    });
};

如需修正此错误,请移除传输结束成员:

library test.good.fi0118;

@transport("Driver")
protocol DriverProtocol {};

@transport("Syscall")
protocol Protocol {
    M();
};

fi-0119

fi-0120:属性位置无效

某些官方属性只能在特定位置使用。例如,@selector 属性只能用于方法:

library test.bad.fi0120a;

@selector("Nonsense")
type MyUnion = union {
    1: hello uint8;
};

如需修正此错误,请移除此属性:

library test.good.fi0120a;

type MyUnion = union {
    1: hello uint8;
};

如果您打算以受支持的方式使用属性,但将其放在了错误的位置,也可能会遇到此错误。例如,@generated_name 属性不能直接应用于成员:

library test.bad.fi0120a;

@selector("Nonsense")
type MyUnion = union {
    1: hello uint8;
};

而是应放在成员的匿名布局之前:

library test.good.fi0120a;

type MyUnion = union {
    1: hello uint8;
};

fi-0121:属性已弃用

部分官方属性已弃用,不应再使用:

library test.bad.fi0121;

@example_deprecated_attribute
type MyStruct = struct {};

修复方法取决于属性被弃用的原因。例如,出错提示可能会提示您改用其他属性。在这种情况下,我们只需移除该属性:

library test.good.fi0121;

type MyStruct = struct {};

fi-0122: Duplicate attribute name

一个元素不能有多个同名属性:

library test.bad.fi0122;

@custom_attribute("first")
@custom_attribute("second")
type Foo = struct {};

而应仅指定每个属性一次:

library test.good.fi0122;

@custom_attribute("first")
type Foo = struct {};

fi-0123:规范属性名称重复

一个元素不能有多个具有相同规范名称的属性:

library test.bad.fi0123;

@custom_attribute("first")
@CustomAttribute("second")
type Foo = struct {};

尽管 custom_attribute 和 CustomAttribute 看起来不同,但它们都由规范名称 custom_attribute 表示。您可以通过将原始名称转换为 snake_case 来获取规范名称。

如需修正此错误,请为每个属性指定一个在规范化后唯一的名称。

library test.good.fi0123;

@custom_attribute("first")
@AnotherCustomAttribute("first")
type Foo = struct {};

如需详细了解 FIDL 为何要求声明具有唯一的规范名称,请参阅 fi-0035。

fi-0124: Custom attribute argument must be string or bool

用户定义的 FIDL 属性的实参仅限于字符串或布尔值类型:

library test.bad.fi0124;

@my_custom_attr(foo=1, bar=2.3)
type MyStruct = struct {};
library test.good.fi0124;

@my_custom_attr(foo=true, bar="baz")
type MyStruct = struct {};

与官方属性不同,编译器不知道用户定义的属性的架构。因此,编译器无法推断任何给定数字参数的类型 - 2 是 int8、uint64 还是 float32?编译器无法得知。

解决此问题的一种可能方法是在 JSON IR 中为这些不明确的情况实现一等 numeric 类型。不过,由于自定义属性参数是目前唯一已知的用例,因此该功能尚未优先处理。

fi-0125:不得为属性实参命名

使用接受单个实参的官方特性时,您无法为该实参命名:

library test.bad.fi0125;

@transport(value="Driver")
protocol Foo {};

而是直接传递实参,不指定名称:

library test.good.fi0125;

@discoverable(name="example.Bar")
protocol Foo {};

FIDL 强制执行此操作,以使属性更加简洁和一致。在后台,参数名称会被推断为 value(并且会显示在 JSON IR 中),因为这是该属性采用的唯一参数。

fi-0126: Attribute argument must be named

使用接受多个实参的官方属性时,您无法传递未命名的实参:

@available(1)
library test.bad.fi0126;

而是指定参数的名称:

@available(added=1)
library test.good.fi0126;

发生此错误的原因是,如果属性接受多个参数,则无法知道您要设置哪个参数。

fi-0127:缺少必需的属性参数

使用具有必需参数的官方属性时,您不能省略该参数:

library test.bad.fi0127;

@has_required_arg
type Foo = struct {};

而是提供必需的参数:

library test.good.fi0127;

@has_required_arg(required="something")
type Foo = struct {};

fi-0128:缺少单个属性参数

使用需要单个参数的官方属性时,您不能省略该参数:

library test.bad.fi0128;

@transport
protocol Protocol {};

而是提供一个参数:

library test.good.fi0128;

@transport("Driver")
protocol Protocol {};

fi-0129: Unknown attribute argument

使用官方属性时,您无法提供不在其架构中的实参:

@available(added=1, discontinued=2)
library test.bad.fi0129;

如果您原本打算传递其他实参,但名称有误,请将其更改为使用正确的名称:

@available(added=1, deprecated=2)
library test.good.fi0129a;

或者,移除该参数:

@available(added=1)
library test.good.fi0129b;

出现此错误的原因是,系统会根据架构验证官方属性。如果 FIDL 允许任意实参,则这些实参将不起作用,并且可能会因掩盖拼写错误而导致 bug。

fi-0130:重复的属性参数

一个属性不能有两个同名的实参:

library test.bad.fi0130;

@custom_attribute(custom_arg=true, custom_arg=true)
type Foo = struct {};

而应仅提供一个具有该名称的实参:

library test.good.fi0130;

@custom_attribute(custom_arg=true)
type Foo = struct {};

fi-0131:规范属性参数重复

一个属性不能有两个具有相同规范名称的实参:

library test.bad.fi0131;

@custom_attribute(custom_arg=true, CustomArg=true)
type Foo = struct {};

尽管 custom_arg 和 CustomArg 看起来不同,但它们都由规范名称 custom_arg 表示。您可以通过将原始名称转换为 snake_case 来获取规范名称。

如需修正此错误,请为每个参数指定一个在规范化后唯一的名称:

library test.good.fi0131a;

@custom_attribute(custom_arg=true, AnotherCustomArg=true)
type Foo = struct {};

或者,移除其中一个参数:

library test.good.fi0131b;

@custom_attribute(custom_arg=true)
type Foo = struct {};

如需详细了解 FIDL 为何要求声明具有唯一的规范名称,请参阅 fi-0035。

fi-0132:属性参数不符合预期

使用不接受实参的官方属性时,您无法提供实参:

library test.bad.fi0132;

type Foo = flexible enum : uint8 {
    @unknown("hello")
    BAR = 1;
};

而是应移除该实参:

library test.good.fi0132;

type Foo = flexible enum : uint8 {
    @unknown
    BAR = 1;
};

fi-0133:属性参数必须是字面量

某些官方属性不允许使用引用常量的实参:

library test.bad.fi0133;

const NAME string = "MyTable";

type Foo = struct {
    bar @generated_name(NAME) table {};
};

而是应将字面量值作为实参传递:

library test.good.fi0133;

type Foo = struct {
    bar @generated_name("MyTable") table {};
};

这些属性需要字面量实参,因为它们的值会影响编译。支持非字面量实参将难以实现,在某些情况下甚至不可能实现,因为这会导致矛盾。

fi-0134

fi-0135:可检测到的名称无效

当您为 @discoverable 属性使用不正确的名称时,会发生此错误。@discoverable 属性应为库名称,后跟 . 和协议名称。

library test.bad.fi0135;

@discoverable(name="test.bad.fi0135/Parser")
protocol Parser {
    Tokenize() -> (struct {
        tokens vector<string>;
    });
};

如需修正此错误,请使用有效的可检测名称:

library test.good.fi0135;

@discoverable(name="test.good.fi0135.Parser")
protocol Parser {
    Tokenize() -> (struct {
        tokens vector<string>;
    });
};

fi-0136

fi-0137

fi-0138

fi-0139

fi-0140

fi-0141:错误类型无效

方法响应载荷上的 error 类型必须是 int32、uint32 或 enum:

library test.bad.fi0141;

protocol MyProtocol {
    MyMethod() -> () error float32;
};

将 error 类型更改为其中一个有效选项,以修正此错误:

library test.good.fi0141;

protocol MyProtocol {
    MyMethod() -> () error int32;
};

如需了解详情,请参阅 RFC-0060:错误处理。

fi-0142:协议传输类型无效

protocol 声明中的 @transport(...) 属性不得指定无效的传输:

library test.bad.fi0142;

@transport("Invalid")
protocol MyProtocol {
    MyMethod();
};

请改用以下任一受支持的传输方式:

library test.good.fi0142;

@transport("Syscall")
protocol MyProtocol {
    MyMethod();
};

我们仍在最终确定哪些传输方式属于受支持的传输方式。如需了解最新信息,请参阅 FIDL 属性。

fi-0143

fi-0144

fi-0145: Attribute typo

如果属性名称的拼写与 FIDL 的某个官方属性过于相似,则会导致编译器警告:

library test.bad.fi0145;

@duc("should be doc")
protocol Example {
    Method();
};

在上面的示例中,属性 @duc 的拼写与官方 FIDL 属性 @doc 太相似。在这种情况下,属性命名是故意为之,而不是官方 FIDL 属性的意外拼写错误,应将其修改为足够唯一:

library test.good.fi0145;

@duck("quack")
protocol Example {
    Method();
};

除了拼写检查之外,此警告的目的还在于不鼓励使用与官方 FIDL 属性过于相似的名称。

拼写错误检测算法的工作原理是计算属性名称与每个官方 FIDL 属性的编辑距离。如果名称过于相似(即编辑距离过小),则会触发拼写错误检测器。

fi-0146:生成的名称无效

当您使用名称无效的 @generated_name 属性时,会发生此错误。生成的名称必须遵循与所有 FIDL 标识符相同的规则。

library test.bad.fi0146;

type Device = table {
    1: kind flexible enum {
        DESKTOP = 1;
        PHONE = 2;
    };
};

type Input = table {
    1: kind @generated_name("_kind") flexible enum {
        KEYBOARD = 1;
        MOUSE = 2;
    };
};

如需解决此问题,请将 @generated_name 值更改为有效标识符。

library test.good.fi0146;

type Device = table {
    1: kind flexible enum {
        DESKTOP = 1;
        PHONE = 2;
    };
};

type Input = table {
    1: kind @generated_name("input_kind") flexible enum {
        KEYBOARD = 1;
        MOUSE = 2;
    };
};

fi-0147:@available 缺少参数

如果您使用 @available 属性,但未提供必要的参数,就会发生此错误。@available 至少需要 added、deprecated 或 removed 中的一个。

@available(added=1)
library test.bad.fi0147;

@available
type Foo = struct {};

如需解决此问题,请添加以下任一必需参数:

@available(added=1)
library test.good.fi0147;

@available(added=2)
type Foo = struct {};

如需了解详情,请参阅 FIDL 版本控制。

fi-0148:不含弃用信息的备注

如果您在没有 deprecated 实参的情况下为 @available 特性使用 note 实参,就会发生此错误。note 仅支持弃用。

@available(added=1, note="My note")
library test.bad.fi0148;

如需修正此错误,请移除备注:

@available(added=1)
library test.good.fi0148a;

或添加必要的 deprecated 声明:

@available(added=1, deprecated=2, note="Removed in 2; use X instead.")
library test.good.fi0148b;

如需了解详情,请参阅 FIDL 版本控制。

fi-0149:平台不在库中

如果您尝试在库声明上方以外的任何位置使用 @available 属性的 platform 参数,就会发生此错误。platform 参数仅在 library 级别有效。

@available(added=1)
library test.bad.fi0149;

@available(platform="foo")
type Person = struct {
    name string;
};

如需解决此问题,请将 platform 实参移至库 @available 属性:

@available(added=1, platform="foo")
library test.good.fi0149a;

type Person = struct {
    name string;
};

或完全移除 platform 参数:

@available(added=1)
library test.good.fi0149b;

type Person = struct {
    name string;
};

fi-0150: Library availability missing added

如果您向库中添加了 @available 属性,但未提供 added 实参,就会发生此错误。library @available 属性需要 added 参数。

@available(removed=2)
library test.bad.fi0150a;
@available(platform="foo")
library test.bad.fi0150b;

如需解决此问题,请将 added 参数添加到库的 @available 属性中:

@available(added=1, removed=2)
library test.good.fi0150a;
@available(added=1, platform="foo")
library test.good.fi0150b;

fi-0151:缺少图书馆可用性

如果您在非 library 声明中添加 @available 属性,但 library 声明中没有 @available 属性,则会发生此错误。

library test.bad.fi0151;

@available(added=1)
type Person = struct {
    name string;
};

如需修正此错误,请将 @available 属性添加到 library 声明中:

@available(added=1)
library test.good.fi0151a;

@available(added=1)
type Person = struct {
    name string;
};

或从非 library 声明中移除 @available 属性:

library test.good.fi0151b;

type Person = struct {
    name string;
};

fi-0152:平台无效

如果您为 @available 属性的 platform 参数使用无效字符,就会发生此错误。platform 参数必须是有效的 FIDL 库标识符。

@available(added=1, platform="Spaces are not allowed")
library test.bad.fi0152;

如需修正此错误,请移除不允许使用的字符:

@available(added=1, platform="foo")
library test.good.fi0152;

fi-0153:版本无效

如果您在 @available 属性中为 added 或 removed 实参使用了无效版本,就会发生此错误。added 和 removed 参数必须是介于 1 到 2^63-1 之间的正整数,或特殊常量 HEAD。

@available(added=0)
library test.bad.fi0153;

如需解决此问题,请将版本更改为有效值:

@available(added=1)
library test.good.fi0153;

fi-0154:库存状况顺序无效

如果您对 @available 属性使用了错误的 added、deprecated 和 remove 参数组合,就会发生此错误。必须遵守以下限制:

  • added 必须小于或等于 deprecated
  • deprecated 必须小于 removed
  • added 必须小于 removed
@available(added=2, removed=2)
library test.bad.fi0154a;
@available(added=2, deprecated=3, removed=3)
library test.bad.fi0154b;

如需解决此问题,请将 added、deprecated 和 removed 参数更新为所需的顺序:

@available(added=1, removed=2)
library test.good.fi0154a;
@available(added=2, deprecated=2, removed=3)
library test.good.fi0154b;

fi-0155:可用性与父级冲突

如果您向非 library 声明添加了与 library 的声明冲突的 @availability 属性,则会发生此错误。

@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155a;

@available(added=1)
type Person = struct {
    name string;
};
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155b;

@available(added=4)
type Person = struct {
    name string;
};

如需修正此错误,请将 @availability 属性更新为所需的限制条件:

@available(added=2, deprecated=3, removed=4)
library test.good.fi0155;

@available(added=2)
type Person = struct {
    name string;
};

fi-0156:不能为可选

当您尝试将无法设为可选的类型标记为可选时,会发生此错误。

library test.bad.fi0156;

type Person = struct {
    name string;
    age int16:optional;
};

如需修正此错误,请移除可选限制条件:

library test.good.fi0156;

type Person = struct {
    name string;
    age int16;
};

只有在不改变传输格式的情况下可以设为可选的 FIDL 类型,才允许使用 optional 限制。如需了解详情,请参阅可选性指南或下方的可展开部分。

FIDL 配方:可选性

通过添加 :optional 约束条件,某些 FIDL 类型可以设为可选,而无需更改其包含的消息的传输格式。此外,table 布局始终是可选的,而 struct 布局则始终不是。若要使 struct 成为可选,必须将其封装在 box<T> 中,从而更改其包含消息的传输形状。

底座类型 可选版本 可选性是否会改变线框布局?
struct {...} box<struct {...}> 是
table {...} table {...} 否
union {...} union {...}:optional 否
vector<T> vector<T>:optional 否
string string:optional 否
zx.Handle zx.Handle:optional 否
client_end:P client_end:<P, optional> 否
server_end:P server_end:<P, optional> 否

所有其他类型(bits、enum、array<T, N> 和基元类型)都不能设为可选。

在此变体中,我们允许键值对存储区将其他键值对存储区作为成员。简而言之,我们将其转换为树。为此,我们将 value 的原始定义替换为使用双成员 union 的定义:一个变体使用与之前相同的 vector<byte> 类型存储叶节点,而另一个变体以其他嵌套存储区的形式存储分支节点。

推理

在这里,我们看到了可选性的几种用法,通过这种用法,我们可以声明可能存在也可能不存在的类型。FIDL 中有三种可选性:

  • 具有这些类型的字段始终在传输过程中不按顺序存储,因此可以通过空信封以内置方式描述“不存在”。为这些类型启用可选性不会影响其所在消息的传输格式,而只会更改该特定类型的有效值。通过添加 :optional 约束条件,union、vector<T>、client_end、server_end 和 zx.Handle 类型都可以设为可选。通过将 value union 设为可选,我们能够以缺少 value 的形式引入规范的“null”条目。这意味着空的 bytes 和缺失/空的 store 属性是无效值。
  • 与上述类型不同,struct 布局没有可用于存储 null 标头的额外空间。因此,它需要封装在信封中,从而改变其所在消息的传输格式。为确保这种修改线路的效果易于理解,Item struct 类型必须封装在 box<T> 类型模板中。
  • 最后,table 布局始终是可选的。缺失的 table 只是一个 未设置任何成员的。

树是一种天然的自引用数据结构:树中的任何节点都可能包含一个具有纯数据(在本例中为字符串)的叶节点,或一个具有更多节点的子树。这需要递归:Item 的定义现在传递依赖于自身!在 FIDL 中表示递归类型可能有点棘手,尤其是因为目前支持有些受限。只要自引用创建的循环中至少有一个可选类型,我们就可以支持此类类型。例如,在这里,我们将 items struct 成员定义为 box<Item>,从而打破了包含循环。

这些更改还大量使用了匿名类型,即在唯一使用点内嵌声明的类型,而不是其自己的命名顶级 type 声明。默认情况下,生成的语言绑定中的匿名类型名称取自其本地上下文。例如,新引入的 flexible union 会采用其所属成员的名称 Value,新引入的 struct 会变为 Store,以此类推。 由于这种启发式方法有时会导致冲突,因此 FIDL 提供了一个应急机制,允许作者手动替换匿名类型的生成名称。这是通过 @generated_name 属性完成的,该属性允许用户更改后端生成的名称。我们可以在这里使用一个,其中原本的 Store 类型被重命名为 NestedStore,以防止与使用相同名称的 protocol 声明发生名称冲突。

实现

FIDL、CML 和领域接口定义修改如下:

FIDL

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library examples.keyvaluestore.supporttrees;

/// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That
/// is, it must start with a letter, end with a letter or number, contain only letters, numbers,
/// periods, and slashes, and be between 4 and 64 characters long.
type Item = struct {
    key string:128;
    value strict union {
        // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the
        // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are
        // disallowed.
        1: bytes vector<byte>:64000;

        // Allows a store within a store, thereby turning our flat key-value store into a tree
        // thereof. Note the use of `@generated_name` to prevent a type-name collision with the
        // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the
        // chain of recursion, thereby allowing `Item` to include itself in its own definition.
        //
        // This is a table so that added fields, like for example a `hash`, can be easily added in
        // the future.
        2: store @generated_name("nested_store") table {
            1: items vector<box<Item>>;
        };
    }:optional;
};

/// An enumeration of things that may go wrong when trying to write a value to our store.
type WriteError = flexible enum {
    UNKNOWN = 0;
    INVALID_KEY = 1;
    INVALID_VALUE = 2;
    ALREADY_EXISTS = 3;
};

/// A very basic key-value store.
@discoverable
open protocol Store {
    /// Writes an item to the store.
    flexible WriteItem(struct {
        attempt Item;
    }) -> () error WriteError;
};

CML

客户端

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/client_bin",
    },
    use: [
        { protocol: "examples.keyvaluestore.supporttrees.Store" },
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteItems",
            key: "write_items",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

        // A newline separated list nested entries. The first line should be the key
        // for the nested store, and each subsequent entry should be a pointer to a text file
        // containing the string value. The name of that text file (without the `.txt` suffix) will
        // serve as the entries key.
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteNested",
            key: "write_nested",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

        // A list of keys, all of which will be populated as null entries.
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteNull",
            key: "write_null",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

    ],
}

服务器

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/server_bin",
    },
    capabilities: [
        { protocol: "examples.keyvaluestore.supporttrees.Store" },
    ],
    expose: [
        {
            protocol: "examples.keyvaluestore.supporttrees.Store",
            from: "self",
        },
    ],
}

领域

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    children: [
        {
            name: "client",
            url: "#meta/client.cm",
        },
        {
            name: "server",
            url: "#meta/server.cm",
        },
    ],
    offer: [
        // Route the protocol under test from the server to the client.
        {
            protocol: "examples.keyvaluestore.supporttrees.Store",
            from: "#server",
            to: "#client",
        },

        // Route diagnostics support to all children.
        {
            dictionary: "diagnostics",
            from: "parent",
            to: [
                "#client",
                "#server",
            ],
        },
    ],
}

然后,您可以使用任何受支持的语言编写客户端和服务器实现:

Rust

客户端

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    anyhow::{Context as _, Error},
    config::Config,
    fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value},
    fuchsia_component::client::connect_to_protocol,
    std::{thread, time},
};

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    println!("Started");

    // Load the structured config values passed to this component at startup.
    let config = Config::take_from_startup_handle();

    // Use the Component Framework runtime to connect to the newly spun up server component. We wrap
    // our retained client end in a proxy object that lets us asynchronously send `Store` requests
    // across the channel.
    let store = connect_to_protocol::<StoreMarker>()?;
    println!("Outgoing connection enabled");

    // This client's structured config has one parameter, a vector of strings. Each string is the
    // path to a resource file whose filename is a key and whose contents are a value. We iterate
    // over them and try to write each key-value pair to the remote store.
    for key in config.write_items.into_iter() {
        let path = format!("/pkg/data/{}.txt", key);
        let value = std::fs::read_to_string(path.clone())
            .with_context(|| format!("Failed to load {path}"))?;
        let res = store
            .write_item(&Item {
                key: key.clone(),
                value: Some(Box::new(Value::Bytes(value.into_bytes()))),
            })
            .await;
        match res? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // Add nested entries to the key-value store as well. The entries are strings, where the first
    // line is the key of the entry, and each subsequent entry should be a pointer to a text file
    // containing the string value. The name of that text file (without the `.txt` suffix) will
    // serve as the entries key.
    for spec in config.write_nested.into_iter() {
        let mut items = vec![];
        let mut nested_store = NestedStore::default();
        let mut lines = spec.split("\n");
        let key = lines.next().unwrap();

        // For each entry, make a new entry in the `NestedStore` being built.
        for entry in lines {
            let path = format!("/pkg/data/{}.txt", entry);
            let contents = std::fs::read_to_string(path.clone())
                .with_context(|| format!("Failed to load {path}"))?;
            items.push(Some(Box::new(Item {
                key: entry.to_string(),
                value: Some(Box::new(Value::Bytes(contents.into()))),
            })));
        }
        nested_store.items = Some(items);

        // Send the `NestedStore`, represented as a vector of values.
        let res = store
            .write_item(&Item {
                key: key.to_string(),
                value: Some(Box::new(Value::Store(nested_store))),
            })
            .await;
        match res? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // Each entry in this list is a null value in the store.
    for key in config.write_null.into_iter() {
        match store.write_item(&Item { key: key.to_string(), value: None }).await? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the
    // referenced bug has been resolved, we can remove the sleep.
    thread::sleep(time::Duration::from_secs(2));
    Ok(())
}

服务器

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Note: For the clarity of this example, allow code to be unused.
#![allow(dead_code)]

use {
    anyhow::{Context as _, Error},
    fidl_examples_keyvaluestore_supporttrees::{
        Item, StoreRequest, StoreRequestStream, Value, WriteError,
    },
    fuchsia_component::server::ServiceFs,
    futures::prelude::*,
    regex::Regex,
    std::cell::RefCell,
    std::collections::HashMap,
    std::collections::hash_map::Entry,
    std::str::from_utf8,
    std::sync::LazyLock,
};

static KEY_VALIDATION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile")
});

// A representation of a key-value store that can contain an arbitrarily deep nesting of other
// key-value stores.
#[allow(clippy::box_collection, reason = "mass allow for https://fxbug.dev/381896734")]
enum StoreNode {
    Leaf(Option<Vec<u8>>),
    Branch(Box<HashMap<String, StoreNode>>),
}

/// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and
/// writes an entry to it.
fn write_item(
    store: &mut HashMap<String, StoreNode>,
    attempt: Item,
    path: &str,
) -> Result<(), WriteError> {
    // Validate the key.
    if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) {
        println!("Write error: INVALID_KEY, For key: {}", attempt.key);
        return Err(WriteError::InvalidKey);
    }

    // Write to the store, validating that the key did not already exist.
    match store.entry(attempt.key) {
        Entry::Occupied(entry) => {
            println!("Write error: ALREADY_EXISTS, For key: {}", entry.key());
            Err(WriteError::AlreadyExists)
        }
        Entry::Vacant(entry) => {
            let key = format!("{}{}", path, entry.key());
            match attempt.value {
                // Null entries are allowed.
                None => {
                    println!("Wrote value: NONE at key: {}", key);
                    entry.insert(StoreNode::Leaf(None));
                }
                Some(value) => match *value {
                    // If this is a nested store, recursively make a new store to insert at this
                    // position.
                    Value::Store(entry_list) => {
                        // Validate the value - absent stores, items lists with no children, or any
                        // of the elements within that list being empty boxes, are all not allowed.
                        if entry_list.items.is_some() {
                            let items = entry_list.items.unwrap();
                            if !items.is_empty() && items.iter().all(|i| i.is_some()) {
                                let nested_path = format!("{}/", key);
                                let mut nested_store = HashMap::<String, StoreNode>::new();
                                for item in items.into_iter() {
                                    write_item(&mut nested_store, *item.unwrap(), &nested_path)?;
                                }

                                println!("Created branch at key: {}", key);
                                entry.insert(StoreNode::Branch(Box::new(nested_store)));
                                return Ok(());
                            }
                        }

                        println!("Write error: INVALID_VALUE, For key: {}", key);
                        return Err(WriteError::InvalidValue);
                    }

                    // This is a simple leaf node on this branch.
                    Value::Bytes(value) => {
                        // Validate the value.
                        if value.is_empty() {
                            println!("Write error: INVALID_VALUE, For key: {}", key);
                            return Err(WriteError::InvalidValue);
                        }

                        println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap());
                        entry.insert(StoreNode::Leaf(Some(value)));
                    }
                },
            }
            Ok(())
        }
    }
}

/// Creates a new instance of the server. Each server has its own bespoke, per-connection instance
/// of the key-value store.
async fn run_server(stream: StoreRequestStream) -> Result<(), Error> {
    // Create a new in-memory key-value store. The store will live for the lifetime of the
    // connection between the server and this particular client.
    let store = RefCell::new(HashMap::<String, StoreNode>::new());

    // Serve all requests on the protocol sequentially - a new request is not handled until its
    // predecessor has been processed.
    stream
        .map(|result| result.context("failed request"))
        .try_for_each(|request| async {
            // Match based on the method being invoked.
            match request {
                StoreRequest::WriteItem { attempt, responder } => {
                    println!("WriteItem request received");

                    // The `responder` parameter is a special struct that manages the outgoing reply
                    // to this method call. Calling `send` on the responder exactly once will send
                    // the reply.
                    responder
                        .send(write_item(&mut store.borrow_mut(), attempt, ""))
                        .context("error sending reply")?;
                    println!("WriteItem response sent");
                }
                StoreRequest::_UnknownMethod { ordinal, .. } => {
                    println!("Received an unknown method with ordinal {ordinal}");
                }
            }
            Ok(())
        })
        .await
}

// A helper enum that allows us to treat a `Store` service instance as a value.
enum IncomingService {
    Store(StoreRequestStream),
}

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    println!("Started");

    // Add a discoverable instance of our `Store` protocol - this will allow the client to see the
    // server and connect to it.
    let mut fs = ServiceFs::new_local();
    fs.dir("svc").add_fidl_service(IncomingService::Store);
    fs.take_and_serve_directory_handle()?;
    println!("Listening for incoming connections");

    // The maximum number of concurrent clients that may be served by this process.
    const MAX_CONCURRENT: usize = 10;

    // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit.
    fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| {
        run_server(stream).unwrap_or_else(|e| println!("{:?}", e))
    })
    .await;

    Ok(())
}

C++(自然)

客户端

// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.

服务器

// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.

C++(有线)

客户端

// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.

服务器

// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.

fi-0157: 客户端/服务器端限制条件必须是协议

应用于 client_end 或 server_end 的第一个限制条件必须指向 protocol 定义:

library test.bad.fi0157;

type MyStruct = struct {};
alias ServerEnd = server_end:MyStruct;

将约束条件更改为指向协议:

library test.good.fi0157;

protocol MyProtocol {};
alias ServerEnd = server_end:MyProtocol;

fi-0158: Cannot bound twice

alias 声明无法更改其别名类型的已设置约束的值:

library test.bad.fi0158;

alias ByteVec256 = vector<uint8>:256;
alias ByteVec512 = ByteVec256:512;

相反,无界限定义应接收其自己的 alias 声明,并且每个进一步受限的别名应依次继承自该声明:

library test.good.fi0158;

alias AliasOfVectorOfString = vector<string>;
alias AliasOfVectorOfStringSmall = AliasOfVectorOfString:8;
alias AliasOfVectorOfStringLarge = AliasOfVectorOfString:16;

为避免混淆和编译器实现复杂性,不允许这样做。

fi-0159: Struct cannot be optional

结构体不能具有 optional 约束条件:

library test.bad.fi0159;

type Date = struct {
    year uint16;
    month uint8;
    day uint8;
};

type Person = struct {
    name string;
    birthday Date:optional;
};

将 T:optional 更改为 box<T> 以解决此问题:

library test.good.fi0159;

type Date = struct {
    year uint16;
    month uint8;
    day uint8;
};

type Person = struct {
    name string;
    birthday box<Date>;
};

只有在不改变传输格式的情况下可以设为可选的 FIDL 类型,才允许使用 optional 限制。如需了解详情,请参阅可选性指南。

fi-0160: Type cannot be marked as optional twice

当某个类型被设为可选两次时,就会发生此错误。通常,当类型在其使用和声明位置都被标记为可选时,就会发生这种情况。

library test.bad.fi0160;

alias MyAlias = vector<string>:optional;

type MyStruct = struct {
    my_member MyAlias:optional;
};

如需修正此错误,请仅将类型设为可选一次。

例如,您可以从使用网站中移除 :optional。

library test.good.fi0160a;

alias MyAlias = vector<string>:optional;

type MyStruct = struct {
    my_member MyAlias;
};

您也可以从别名声明中移除 :optional。

library test.good.fi0160b;

alias MyAlias = vector<string>;

type MyStruct = struct {
    my_member MyAlias:optional;
};

fi-0161:大小不得为零

当您尝试将数组大小限制设置为 0 时,会发生此错误。数组的大小不能为零。

library test.bad.fi0161;

type Person = struct {
    name string;
    nicknames array<string, 0>;
};

如需修正此错误,请将大小限制更改为正整数。

library test.good.fi0161;

type Person = struct {
    name string;
    nicknames array<string, 5>;
};

fi-0162:布局参数数量有误

某些 FIDL 布局(例如 vector 和 array)接受形参。此错误表示突出显示的类型指定的参数数量不正确:

library test.bad.fi0162a;

type Foo = struct {
    bar array<8>;
};

如果不可参数化类型错误地附加了参数,也可能会出现此错误:

library test.bad.fi0162b;

type Foo = struct {
    bar uint8<8>;
};

解决方法始终是为有问题的布局指定正确数量的参数:

library test.good.fi0162;

type Foo = struct {
    bar array<uint8, 8>;
};

FIDL 中唯一的形参化类型是 array<T, N>、box<T> 和 vector<T>。client_end 和 server_end 类型过去在旧版 FIDL 语法中是参数化的,但现在不再是这样,尽管这是导致此错误的常见原因。现在,这两种类型改为将其协议规范作为(必需)限制条件。

形参始终列在尖括号 <...> 内,与约束条件有一些相似之处,约束条件显示在类型末尾的 :... 字符后面。例如,乍一看,array<T, N> 将其大小指定为参数,而 vector<T>:N 将其大小指定为约束条件,这可能看起来很奇怪。区别在于,形参始终会影响相关类型的传输布局形状,而约束条件只会更改在编码/解码时被视为可接受的值集,对传输布局没有影响。

如需更全面地讨论这两个概念之间的区别,请参阅 RFC-0050:FIDL 语法改进。

fi-0163:多个限制条件定义

当您尝试使用多个冒号 (:) 定义多个限制条件定义时,会发生此错误。多个限制条件定义必须使用尖括号语法 type:<constraint1, constraint2, etc>。

library test.bad.fi0163;

type Person = struct {
  name string;
  favorite_color string:30:optional;
};

如需修正此错误,请使用尖括号语法来指定约束条件:

library test.good.fi0163;

type Person = struct {
    name string;
    favorite_color string:<30, optional>;
};

fi-0164:限制过多

当您尝试向某个类型添加超出支持数量的限制条件时,会发生此错误。例如,string 最多支持两个限制条件。

library test.bad.fi0164;

type Person = struct {
    name string:<0, optional, 20>;
};

如需解决此问题,请移除多余的限制条件:

library test.good.fi0164;

type Person = struct {
    name string:<20, optional>;
};

fi-0165: Expected type

当您在 FIDL 预期类型时使用常量或协议标识符时,会发生此错误。

library test.bad.fi0165;

type Person = struct {
    name string;
    nicknames vector<5>;
};

如需修正此错误,请更新代码以使用有效类型:

library test.good.fi0165;

type Person = struct {
    name string;
    nicknames vector<string>:5;
};

协议不被视为 FIDL 类型,也不能在需要类型的地方使用。

fi-0166:意外的限制条件

当您尝试在不应使用限制条件的地方使用限制条件时,会发生此错误。这通常是因为命名的 const 位于错误的位置。

library test.bad.fi0166;

const MIN_SIZE uint8 = 1;
const MAX_SIZE uint8 = 5;

type Person = struct {
    name string;
    nicknames vector<string>:<MIN_SIZE, MAX_SIZE>;
};

如需修正此错误,请移除该限制条件:

library test.good.fi0166;

const MAX_SIZE uint8 = 5;

type Person = struct {
    name string;
    nicknames vector<string>:<MAX_SIZE>;
};

fi-0167:无法约束两次

禁止重新分配已通过 alias 声明定义了传输绑定的 client_end 或 server_end 的传输绑定:

library test.bad.fi0167;

protocol MyOtherProtocol {};

alias ClientEnd = client_end:MyProtocol;
alias ServerEnd = server_end:MyProtocol;

protocol MyProtocol {
    MyMethod(resource struct {
        my_client ClientEnd:MyOtherProtocol;
    }) -> (resource struct {
        my_server ServerEnd:MyOtherProtocol;
    });
};

相反,应完全避免 client_end 和 server_end 类型的别名:

library test.good.fi0167;

protocol MyProtocol {
    MyMethod(resource struct {
        my_client client_end:MyProtocol;
    }) -> (resource struct {
        my_server server_end:MyProtocol;
    });
};

为避免混淆和编译器实现复杂性,不允许这样做。

fi-0168: Client/server end must have protocol constraint

应用于 client_end 或 server_end 的第一个限制条件必须指向 protocol 定义:

library test.bad.fi0168;

protocol MyProtocol {
    MyMethod(resource struct {
        server server_end;
    });
};

添加指向所需协议的限制条件:

library test.good.fi0168;

protocol MyProtocol {
    MyMethod(resource struct {
        server server_end:MyProtocol;
    });
};

fi-0169: Boxed type cannot be optional

box<T> 形式的类型不能应用 optional 限制条件:

library test.bad.fi0169;

type Color = struct {
    red byte;
    green byte;
    blue byte;
};

type MyStruct = struct {
    maybe_color box<Color>:optional;
};

根据定义,装箱类型是可选的,因此添加额外限制是不必要且多余的:

library test.good.fi0169;

type Color = struct {
    red byte;
    green byte;
    blue byte;
};

type MyStruct = struct {
    maybe_color box<Color>;
};

fi-0170

fi-0171:装箱类型应改用可选约束条件

只有使用 struct 布局的类型才能装箱;union、vector、string、client_end、server_end 和 zx.Handle 必须改用 optional 约束条件:

library test.bad.fi0171;

using zx;

type MyStruct = resource struct {
    my_resource_member box<zx.Handle>;
};

将 box<T> 转换为 T:optional 即可解决此问题:

library test.good.fi0171;

using zx;

type MyStruct = resource struct {
    my_resource_member zx.Handle:optional;
};

只有在不改变传输格式的情况下可以设为可选的 FIDL 类型,才允许使用 optional 限制。如需了解详情,请参阅可选性指南或下方的可展开部分。

FIDL 配方:可选性

通过添加 :optional 约束条件,某些 FIDL 类型可以设为可选,而无需更改其包含的消息的传输格式。此外,table 布局始终是可选的,而 struct 布局则始终不是。若要使 struct 成为可选,必须将其封装在 box<T> 中,从而更改其包含消息的传输形状。

底座类型 可选版本 可选性是否会改变线框布局?
struct {...} box<struct {...}> 是
table {...} table {...} 否
union {...} union {...}:optional 否
vector<T> vector<T>:optional 否
string string:optional 否
zx.Handle zx.Handle:optional 否
client_end:P client_end:<P, optional> 否
server_end:P server_end:<P, optional> 否

所有其他类型(bits、enum、array<T, N> 和基元类型)都不能设为可选。

在此变体中,我们允许键值对存储区将其他键值对存储区作为成员。简而言之,我们将其转换为树。为此,我们将 value 的原始定义替换为使用双成员 union 的定义:一个变体使用与之前相同的 vector<byte> 类型存储叶节点,而另一个变体以其他嵌套存储区的形式存储分支节点。

推理

在这里,我们看到了可选性的几种用法,通过这种用法,我们可以声明可能存在也可能不存在的类型。FIDL 中有三种可选性:

  • 具有这些类型的字段始终在传输过程中不按顺序存储,因此可以通过空信封以内置方式描述“不存在”。为这些类型启用可选性不会影响其所在消息的传输格式,而只会更改该特定类型的有效值。通过添加 :optional 约束条件,union、vector<T>、client_end、server_end 和 zx.Handle 类型都可以设为可选。通过将 value union 设为可选,我们能够以缺少 value 的形式引入规范的“null”条目。这意味着空的 bytes 和缺失/空的 store 属性是无效值。
  • 与上述类型不同,struct 布局没有可用于存储 null 标头的额外空间。因此,它需要封装在信封中,从而改变其所在消息的传输格式。为确保这种修改线路的效果易于理解,Item struct 类型必须封装在 box<T> 类型模板中。
  • 最后,table 布局始终是可选的。缺失的 table 只是一个 未设置任何成员的。

树是一种天然的自引用数据结构:树中的任何节点都可能包含一个具有纯数据(在本例中为字符串)的叶节点,或一个具有更多节点的子树。这需要递归:Item 的定义现在传递依赖于自身!在 FIDL 中表示递归类型可能有点棘手,尤其是因为目前支持有些受限。只要自引用创建的循环中至少有一个可选类型,我们就可以支持此类类型。例如,在这里,我们将 items struct 成员定义为 box<Item>,从而打破了包含循环。

这些更改还大量使用了匿名类型,即在唯一使用点内嵌声明的类型,而不是其自己的命名顶级 type 声明。默认情况下,生成的语言绑定中的匿名类型名称取自其本地上下文。例如,新引入的 flexible union 会采用其所属成员的名称 Value,新引入的 struct 会变为 Store,以此类推。 由于这种启发式方法有时会导致冲突,因此 FIDL 提供了一个应急机制,允许作者手动替换匿名类型的生成名称。这是通过 @generated_name 属性完成的,该属性允许用户更改后端生成的名称。我们可以在这里使用一个,其中原本的 Store 类型被重命名为 NestedStore,以防止与使用相同名称的 protocol 声明发生名称冲突。

实现

FIDL、CML 和领域接口定义修改如下:

FIDL

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library examples.keyvaluestore.supporttrees;

/// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That
/// is, it must start with a letter, end with a letter or number, contain only letters, numbers,
/// periods, and slashes, and be between 4 and 64 characters long.
type Item = struct {
    key string:128;
    value strict union {
        // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the
        // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are
        // disallowed.
        1: bytes vector<byte>:64000;

        // Allows a store within a store, thereby turning our flat key-value store into a tree
        // thereof. Note the use of `@generated_name` to prevent a type-name collision with the
        // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the
        // chain of recursion, thereby allowing `Item` to include itself in its own definition.
        //
        // This is a table so that added fields, like for example a `hash`, can be easily added in
        // the future.
        2: store @generated_name("nested_store") table {
            1: items vector<box<Item>>;
        };
    }:optional;
};

/// An enumeration of things that may go wrong when trying to write a value to our store.
type WriteError = flexible enum {
    UNKNOWN = 0;
    INVALID_KEY = 1;
    INVALID_VALUE = 2;
    ALREADY_EXISTS = 3;
};

/// A very basic key-value store.
@discoverable
open protocol Store {
    /// Writes an item to the store.
    flexible WriteItem(struct {
        attempt Item;
    }) -> () error WriteError;
};

CML

客户端

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/client_bin",
    },
    use: [
        { protocol: "examples.keyvaluestore.supporttrees.Store" },
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteItems",
            key: "write_items",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

        // A newline separated list nested entries. The first line should be the key
        // for the nested store, and each subsequent entry should be a pointer to a text file
        // containing the string value. The name of that text file (without the `.txt` suffix) will
        // serve as the entries key.
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteNested",
            key: "write_nested",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

        // A list of keys, all of which will be populated as null entries.
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteNull",
            key: "write_null",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

    ],
}

服务器

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/server_bin",
    },
    capabilities: [
        { protocol: "examples.keyvaluestore.supporttrees.Store" },
    ],
    expose: [
        {
            protocol: "examples.keyvaluestore.supporttrees.Store",
            from: "self",
        },
    ],
}

领域

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    children: [
        {
            name: "client",
            url: "#meta/client.cm",
        },
        {
            name: "server",
            url: "#meta/server.cm",
        },
    ],
    offer: [
        // Route the protocol under test from the server to the client.
        {
            protocol: "examples.keyvaluestore.supporttrees.Store",
            from: "#server",
            to: "#client",
        },

        // Route diagnostics support to all children.
        {
            dictionary: "diagnostics",
            from: "parent",
            to: [
                "#client",
                "#server",
            ],
        },
    ],
}

然后,您可以使用任何受支持的语言编写客户端和服务器实现:

Rust

客户端

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    anyhow::{Context as _, Error},
    config::Config,
    fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value},
    fuchsia_component::client::connect_to_protocol,
    std::{thread, time},
};

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    println!("Started");

    // Load the structured config values passed to this component at startup.
    let config = Config::take_from_startup_handle();

    // Use the Component Framework runtime to connect to the newly spun up server component. We wrap
    // our retained client end in a proxy object that lets us asynchronously send `Store` requests
    // across the channel.
    let store = connect_to_protocol::<StoreMarker>()?;
    println!("Outgoing connection enabled");

    // This client's structured config has one parameter, a vector of strings. Each string is the
    // path to a resource file whose filename is a key and whose contents are a value. We iterate
    // over them and try to write each key-value pair to the remote store.
    for key in config.write_items.into_iter() {
        let path = format!("/pkg/data/{}.txt", key);
        let value = std::fs::read_to_string(path.clone())
            .with_context(|| format!("Failed to load {path}"))?;
        let res = store
            .write_item(&Item {
                key: key.clone(),
                value: Some(Box::new(Value::Bytes(value.into_bytes()))),
            })
            .await;
        match res? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // Add nested entries to the key-value store as well. The entries are strings, where the first
    // line is the key of the entry, and each subsequent entry should be a pointer to a text file
    // containing the string value. The name of that text file (without the `.txt` suffix) will
    // serve as the entries key.
    for spec in config.write_nested.into_iter() {
        let mut items = vec![];
        let mut nested_store = NestedStore::default();
        let mut lines = spec.split("\n");
        let key = lines.next().unwrap();

        // For each entry, make a new entry in the `NestedStore` being built.
        for entry in lines {
            let path = format!("/pkg/data/{}.txt", entry);
            let contents = std::fs::read_to_string(path.clone())
                .with_context(|| format!("Failed to load {path}"))?;
            items.push(Some(Box::new(Item {
                key: entry.to_string(),
                value: Some(Box::new(Value::Bytes(contents.into()))),
            })));
        }
        nested_store.items = Some(items);

        // Send the `NestedStore`, represented as a vector of values.
        let res = store
            .write_item(&Item {
                key: key.to_string(),
                value: Some(Box::new(Value::Store(nested_store))),
            })
            .await;
        match res? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // Each entry in this list is a null value in the store.
    for key in config.write_null.into_iter() {
        match store.write_item(&Item { key: key.to_string(), value: None }).await? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the
    // referenced bug has been resolved, we can remove the sleep.
    thread::sleep(time::Duration::from_secs(2));
    Ok(())
}

服务器

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Note: For the clarity of this example, allow code to be unused.
#![allow(dead_code)]

use {
    anyhow::{Context as _, Error},
    fidl_examples_keyvaluestore_supporttrees::{
        Item, StoreRequest, StoreRequestStream, Value, WriteError,
    },
    fuchsia_component::server::ServiceFs,
    futures::prelude::*,
    regex::Regex,
    std::cell::RefCell,
    std::collections::HashMap,
    std::collections::hash_map::Entry,
    std::str::from_utf8,
    std::sync::LazyLock,
};

static KEY_VALIDATION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile")
});

// A representation of a key-value store that can contain an arbitrarily deep nesting of other
// key-value stores.
#[allow(clippy::box_collection, reason = "mass allow for https://fxbug.dev/381896734")]
enum StoreNode {
    Leaf(Option<Vec<u8>>),
    Branch(Box<HashMap<String, StoreNode>>),
}

/// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and
/// writes an entry to it.
fn write_item(
    store: &mut HashMap<String, StoreNode>,
    attempt: Item,
    path: &str,
) -> Result<(), WriteError> {
    // Validate the key.
    if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) {
        println!("Write error: INVALID_KEY, For key: {}", attempt.key);
        return Err(WriteError::InvalidKey);
    }

    // Write to the store, validating that the key did not already exist.
    match store.entry(attempt.key) {
        Entry::Occupied(entry) => {
            println!("Write error: ALREADY_EXISTS, For key: {}", entry.key());
            Err(WriteError::AlreadyExists)
        }
        Entry::Vacant(entry) => {
            let key = format!("{}{}", path, entry.key());
            match attempt.value {
                // Null entries are allowed.
                None => {
                    println!("Wrote value: NONE at key: {}", key);
                    entry.insert(StoreNode::Leaf(None));
                }
                Some(value) => match *value {
                    // If this is a nested store, recursively make a new store to insert at this
                    // position.
                    Value::Store(entry_list) => {
                        // Validate the value - absent stores, items lists with no children, or any
                        // of the elements within that list being empty boxes, are all not allowed.
                        if entry_list.items.is_some() {
                            let items = entry_list.items.unwrap();
                            if !items.is_empty() && items.iter().all(|i| i.is_some()) {
                                let nested_path = format!("{}/", key);
                                let mut nested_store = HashMap::<String, StoreNode>::new();
                                for item in items.into_iter() {
                                    write_item(&mut nested_store, *item.unwrap(), &nested_path)?;
                                }

                                println!("Created branch at key: {}", key);
                                entry.insert(StoreNode::Branch(Box::new(nested_store)));
                                return Ok(());
                            }
                        }

                        println!("Write error: INVALID_VALUE, For key: {}", key);
                        return Err(WriteError::InvalidValue);
                    }

                    // This is a simple leaf node on this branch.
                    Value::Bytes(value) => {
                        // Validate the value.
                        if value.is_empty() {
                            println!("Write error: INVALID_VALUE, For key: {}", key);
                            return Err(WriteError::InvalidValue);
                        }

                        println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap());
                        entry.insert(StoreNode::Leaf(Some(value)));
                    }
                },
            }
            Ok(())
        }
    }
}

/// Creates a new instance of the server. Each server has its own bespoke, per-connection instance
/// of the key-value store.
async fn run_server(stream: StoreRequestStream) -> Result<(), Error> {
    // Create a new in-memory key-value store. The store will live for the lifetime of the
    // connection between the server and this particular client.
    let store = RefCell::new(HashMap::<String, StoreNode>::new());

    // Serve all requests on the protocol sequentially - a new request is not handled until its
    // predecessor has been processed.
    stream
        .map(|result| result.context("failed request"))
        .try_for_each(|request| async {
            // Match based on the method being invoked.
            match request {
                StoreRequest::WriteItem { attempt, responder } => {
                    println!("WriteItem request received");

                    // The `responder` parameter is a special struct that manages the outgoing reply
                    // to this method call. Calling `send` on the responder exactly once will send
                    // the reply.
                    responder
                        .send(write_item(&mut store.borrow_mut(), attempt, ""))
                        .context("error sending reply")?;
                    println!("WriteItem response sent");
                }
                StoreRequest::_UnknownMethod { ordinal, .. } => {
                    println!("Received an unknown method with ordinal {ordinal}");
                }
            }
            Ok(())
        })
        .await
}

// A helper enum that allows us to treat a `Store` service instance as a value.
enum IncomingService {
    Store(StoreRequestStream),
}

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    println!("Started");

    // Add a discoverable instance of our `Store` protocol - this will allow the client to see the
    // server and connect to it.
    let mut fs = ServiceFs::new_local();
    fs.dir("svc").add_fidl_service(IncomingService::Store);
    fs.take_and_serve_directory_handle()?;
    println!("Listening for incoming connections");

    // The maximum number of concurrent clients that may be served by this process.
    const MAX_CONCURRENT: usize = 10;

    // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit.
    fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| {
        run_server(stream).unwrap_or_else(|e| println!("{:?}", e))
    })
    .await;

    Ok(())
}

C++(自然)

客户端

// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.

服务器

// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.

C++(有线)

客户端

// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.

服务器

// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.

fi-0172:资源定义必须使用 uint32 子类型

resource_definition 声明的子类型必须为 uint32:

library test.bad.fi0172;

type MySubtype = strict enum : uint32 {
    NONE = 0;
};

resource_definition MyResource : uint8 {
    properties {
        subtype MySubtype;
    };
};

将子类型更改为 uint32 以修正此错误:

library test.good.fi0172;

type MySubtype = strict enum : uint32 {
    NONE = 0;
};

resource_definition MyResource : uint32 {
    properties {
        subtype MySubtype;
    };
};

这是与 FIDL 内部实现相关的错误,因此只应向开发 FIDL 核心库的开发者显示。最终用户不应看到此错误。

它引用的 resource_definition 声明是 FIDL 定义句柄等资源的内部方法,未来可能会随着句柄泛化工作而发生变化。

fi-0173: Resource definition must specify subtype

resource_definition 声明不能省略 subtype 成员:

library test.bad.fi0173;

resource_definition MyResource : uint32 {
    properties {
        rights uint32;
    };
};

请将此成员指向有效的 enum : uint32 声明:

library test.good.fi0173;

resource_definition MyResource : uint32 {
    properties {
        subtype flexible enum : uint32 {};
        rights uint32;
    };
};

这是与 FIDL 内部实现相关的错误,因此只应向开发 FIDL 核心库的开发者显示。最终用户不应看到此错误。

它引用的 resource_definition 声明是 FIDL 定义句柄等资源的内部方法,未来可能会随着句柄泛化工作而发生变化。

fi-0174

fi-0175: Resource definition subtype property must refer to enum

resource_definition 声明不能使用非 enum 作为 subtype 成员:

library test.bad.fi0175;

resource_definition MyResource : uint32 {
    properties {
        subtype struct {};
    };
};

请将此成员指向有效的 enum : uint32 声明:

library test.good.fi0175;

resource_definition MyResource : uint32 {
    properties {
        subtype flexible enum : uint32 {};
    };
};

这是与 FIDL 内部实现相关的错误,因此只应向开发 FIDL 核心库的开发者显示。最终用户不应看到此错误。

它引用的 resource_definition 声明是 FIDL 定义句柄等资源的内部方法,未来可能会随着句柄泛化工作而发生变化。

fi-0176

fi-0177:资源定义权限属性必须引用位

resource_definition 声明不能使用非 bits 作为 rights 成员:

library test.bad.fi0177;

type MySubtype = enum : uint32 {
    NONE = 0;
    VMO = 3;
};

resource_definition MyResource : uint32 {
    properties {
        subtype MySubtype;
        rights string;
    };
};

请将此成员指向有效的 bits : uint32 声明:

library test.good.fi0177;

type MySubtype = enum : uint32 {
    NONE = 0;
    VMO = 3;
};

resource_definition MyResource : uint32 {
    properties {
        subtype MySubtype;
        rights uint32;
    };
};

这是与 FIDL 内部实现相关的错误,因此只应向开发 FIDL 核心库的开发者显示。最终用户不应看到此错误。

它引用的 resource_definition 声明是 FIDL 定义句柄等资源的内部方法,未来可能会随着句柄泛化工作而发生变化。

fi-0178:未使用的导入

不引用通过 using 声明导入的依赖项属于错误:

library test.bad.fi0178;

using dependent;

type Foo = struct {
    does_not int64;
    use_dependent int32;
};

请确保在导入库时使用了所有此类导入,方法是实际引用导入或移除未使用的依赖项:

library test.good.fi0178;

using dependent;

type Foo = struct {
    dep dependent.Bar;
};

fi-0179: Newtypes cannot be constrained

RFC-0052:类型别名和新类型中的新类型不允许受到限制。例如,string 的新类型不能使用 :optional 进行限制:

library test.bad.fi0179;

type Name = string;

type Info = struct {
    name Name:optional;
};

在这种情况下,我们可以将 name 字段放入表中(而不是结构体中),使其成为可选字段:

library test.good.fi0179;

type Name = string;

type Info = table {
    1: name Name;
};

此限制简化了新类型的设计。目前尚不清楚受限新类型的 API 和 ABI 应该是什么样子(例如,限制应该应用于新类型本身,还是流向底层类型?)。

fi-0180:Zircon C 类型处于实验阶段

内置类型 usize、uintptr、uchar 和 experimental_pointer 正在针对 Zither 项目进行开发。它们不能在普通 FIDL 库中使用:

library test.bad.fi0180;

type Data = struct {
    size usize64;
};

请改用其他类型,例如使用 uint64 而不是 usize:

library test.good.fi0180;

type Data = struct {
    size uint64;
};

fi-0181:库属性实参引用了常量

库声明中的属性参数不允许引用常量:

@custom_attribute(VALUE)
library test.bad.fi0181a;

const VALUE string = "hello";

而是应提供字面量实参:

@custom_attribute("hello")
library test.good.fi0181a;

之所以存在此限制,是因为很少需要这样做,而且支持此操作会给编译器带来不必要的复杂性。

fi-0182

fi-0183

fi-0184:意外的控制字符

字符串字面量不得包含原始控制字符(ASCII 字符 0x00 到 0x1f):

library test.bad.fi0184;

const TAB string = "	"; // literal tab character

请改用转义序列。在本例中,\t 是正确的:

library test.good.fi0184a;

const TAB string = "\t";

或者,您也可以使用 Unicode 转义序列。这适用于任何 Unicode 码位:

library test.good.fi0184b;

const TAB string = "\u{9}";

字符串字面量中不允许使用原始控制字符,因为它们都是空白字符或不可打印字符,因此如果直接嵌入到 FIDL 源文件中,会令人困惑且难以注意到。

fi-0185:Unicode 转义序列缺少大括号

字符串字面量中的 Unicode 转义序列必须在大括号中指定码位:

library test.bad.fi0185;

const SMILE string = "\u";

如需修正此错误,请在大括号中指定码位:

library test.good.fi0185;

const SMILE string = "\u{1F600}";

fi-0186: Unterminated Unicode escape sequence

字符串字面量中的 Unicode 转义序列必须终止:

library test.bad.fi0186;

const SMILE string = "\u{1F600";

如需终止转义序列,请添加右大括号 }:

library test.good.fi0186;

const SMILE string = "\u{1F600}";

fi-0187:Unicode 转义序列为空

字符串字面量中的 Unicode 转义序列必须至少有一个十六进制数字:

library test.bad.fi0187;

const SMILE string = "\u{}";

如需修正此错误,请添加十六进制数字以指定 Unicode 码位:

library test.good.fi0187;

const SMILE string = "\u{1F600}";

fi-0188: Too many digits in Unicode escape sequence

字符串字面量中的 Unicode 转义序列不能超过 6 个十六进制数字:

library test.bad.fi0188;

const SMILE string = "\u{001F600}";

如需修正此错误,请最多指定 6 位十六进制数字。在本例中,我们可以移除前导零:

library test.good.fi0188;

const SMILE string = "\u{1F600}";

之所以存在此限制,是因为所有有效的 Unicode 码位都适合 6 位十六进制数字,因此没有理由允许使用更多数字。

fi-0189: Unicode code point too large

字符串字面量中的 Unicode 转义序列不能指定大于 0x10ffff 最大值的 Unicode 码位:

library test.bad.fi0189;

const TOO_LARGE string = "\u{110000}";

而是应确保码位有效:

library test.good.fi0189;

const MAX_CODEPOINT string = "\u{10ffff}";

fi-0190

fi-0191:方法必须指定严格性

此错误表示 FIDL 方法没有 strict 或 flexible 修饰符。

library test.bad.fi0191;

open protocol Example {
    OneWay();
};

如需解决此问题,请向该方法添加 strict 或 flexible。如果这是现有方法,您必须使用 strict,并且应参阅兼容性指南,了解如何将其更改为 flexible。如果这是一种新方法,您应参阅 API 准则,了解如何选择。

library test.good.fi0191;

open protocol Example {
    flexible OneWay();
};

FIDL 目前正在进行迁移,以支持处理 RFC-0138 中定义的未知互动。这项新功能允许将修饰符 strict 和 flexible 应用于 FIDL 方法和事件。过去,所有方法的行为都类似于 strict,但在本次迁移结束时,默认值将为 flexible。为避免因将方法默认修饰符从 strict 更改为 flexible 而引起混淆和可能出现的问题,在此过渡期内,方法修饰符是必需的。迁移完成后,此错误将变为 Linter 建议。

如需详细了解未知互动,请参阅 FIDL 语言参考文档。

fi-0192:协议必须指定开放性

此错误表示 FIDL 协议没有 open、ajar 或 closed 修饰符。

library test.bad.fi0192;

protocol ImplicitOpenness {};

如需解决此问题,请将 open、ajar 或 closed 添加到协议中。如果这是现有协议,您必须使用 closed,并且应参阅兼容性指南,了解如何将其更改为 open 或 ajar。如果这是一种新方法,您应该参阅 API 准则,了解如何选择。

library test.good.fi0192;

open protocol ImplicitOpenness {};

FIDL 目前正在进行迁移,以支持处理 RFC-0138 中定义的未知互动。这项新功能添加了三个新修饰符:open、ajar 和 closed,它们适用于 FIDL 协议。过去,所有协议的行为都与 closed 相同,但在本次迁移结束时,默认值将变为 open。为避免因将协议默认修饰符从 closed 更改为 open 而引起混淆和可能出现的问题,在此过渡期内,协议修饰符是必需的。迁移完成后,此错误将变为 Linter 建议。

如需详细了解未知互动,请参阅 FIDL 语言参考文档。

fi-0193:无法装箱类型

结构体以外的类型无法装箱。例如,基元类型无法装箱:

library test.bad.fi0193;

type MyStruct = struct {
    my_member box<bool>;
};

如需对基元进行装箱,请将其放入单成员 struct 中:

library test.good.fi0193;

type MyStruct = struct {
    my_member box<struct {
        my_bool bool;
    }>;
};

请注意,通过使用 optional 约束条件,某些类型可以设为可选。如需了解详情,请参阅可选性指南或下方的可展开部分。

FIDL 配方:可选性

通过添加 :optional 约束条件,某些 FIDL 类型可以设为可选,而无需更改其包含的消息的传输格式。此外,table 布局始终是可选的,而 struct 布局则始终不是。若要使 struct 成为可选,必须将其封装在 box<T> 中,从而更改其包含消息的传输形状。

底座类型 可选版本 可选性是否会改变线框布局?
struct {...} box<struct {...}> 是
table {...} table {...} 否
union {...} union {...}:optional 否
vector<T> vector<T>:optional 否
string string:optional 否
zx.Handle zx.Handle:optional 否
client_end:P client_end:<P, optional> 否
server_end:P server_end:<P, optional> 否

所有其他类型(bits、enum、array<T, N> 和基元类型)都不能设为可选。

在此变体中,我们允许键值对存储区将其他键值对存储区作为成员。简而言之,我们将其转换为树。为此,我们将 value 的原始定义替换为使用双成员 union 的定义:一个变体使用与之前相同的 vector<byte> 类型存储叶节点,而另一个变体以其他嵌套存储区的形式存储分支节点。

推理

在这里,我们看到了可选性的几种用法,通过这种用法,我们可以声明可能存在也可能不存在的类型。FIDL 中有三种可选性:

  • 具有这些类型的字段始终在传输过程中不按顺序存储,因此可以通过空信封以内置方式描述“不存在”。为这些类型启用可选性不会影响其所在消息的传输格式,而只会更改该特定类型的有效值。通过添加 :optional 约束条件,union、vector<T>、client_end、server_end 和 zx.Handle 类型都可以设为可选。通过将 value union 设为可选,我们能够以缺少 value 的形式引入规范的“null”条目。这意味着空的 bytes 和缺失/空的 store 属性是无效值。
  • 与上述类型不同,struct 布局没有可用于存储 null 标头的额外空间。因此,它需要封装在信封中,从而改变其所在消息的传输格式。为确保这种修改线路的效果易于理解,Item struct 类型必须封装在 box<T> 类型模板中。
  • 最后,table 布局始终是可选的。缺失的 table 只是一个 未设置任何成员的。

树是一种天然的自引用数据结构:树中的任何节点都可能包含一个具有纯数据(在本例中为字符串)的叶节点,或一个具有更多节点的子树。这需要递归:Item 的定义现在传递依赖于自身!在 FIDL 中表示递归类型可能有点棘手,尤其是因为目前支持有些受限。只要自引用创建的循环中至少有一个可选类型,我们就可以支持此类类型。例如,在这里,我们将 items struct 成员定义为 box<Item>,从而打破了包含循环。

这些更改还大量使用了匿名类型,即在唯一使用点内嵌声明的类型,而不是其自己的命名顶级 type 声明。默认情况下,生成的语言绑定中的匿名类型名称取自其本地上下文。例如,新引入的 flexible union 会采用其所属成员的名称 Value,新引入的 struct 会变为 Store,以此类推。 由于这种启发式方法有时会导致冲突,因此 FIDL 提供了一个应急机制,允许作者手动替换匿名类型的生成名称。这是通过 @generated_name 属性完成的,该属性允许用户更改后端生成的名称。我们可以在这里使用一个,其中原本的 Store 类型被重命名为 NestedStore,以防止与使用相同名称的 protocol 声明发生名称冲突。

实现

FIDL、CML 和领域接口定义修改如下:

FIDL

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library examples.keyvaluestore.supporttrees;

/// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That
/// is, it must start with a letter, end with a letter or number, contain only letters, numbers,
/// periods, and slashes, and be between 4 and 64 characters long.
type Item = struct {
    key string:128;
    value strict union {
        // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the
        // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are
        // disallowed.
        1: bytes vector<byte>:64000;

        // Allows a store within a store, thereby turning our flat key-value store into a tree
        // thereof. Note the use of `@generated_name` to prevent a type-name collision with the
        // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the
        // chain of recursion, thereby allowing `Item` to include itself in its own definition.
        //
        // This is a table so that added fields, like for example a `hash`, can be easily added in
        // the future.
        2: store @generated_name("nested_store") table {
            1: items vector<box<Item>>;
        };
    }:optional;
};

/// An enumeration of things that may go wrong when trying to write a value to our store.
type WriteError = flexible enum {
    UNKNOWN = 0;
    INVALID_KEY = 1;
    INVALID_VALUE = 2;
    ALREADY_EXISTS = 3;
};

/// A very basic key-value store.
@discoverable
open protocol Store {
    /// Writes an item to the store.
    flexible WriteItem(struct {
        attempt Item;
    }) -> () error WriteError;
};

CML

客户端

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/client_bin",
    },
    use: [
        { protocol: "examples.keyvaluestore.supporttrees.Store" },
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteItems",
            key: "write_items",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

        // A newline separated list nested entries. The first line should be the key
        // for the nested store, and each subsequent entry should be a pointer to a text file
        // containing the string value. The name of that text file (without the `.txt` suffix) will
        // serve as the entries key.
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteNested",
            key: "write_nested",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

        // A list of keys, all of which will be populated as null entries.
        {
            config: "fuchsia.examples.keyvaluestore.supporttrees.WriteNull",
            key: "write_null",
            type: "vector",
            max_count: 16,
            element: {
                type: "string",
                max_size: 64,
            },
            availability: "transitional",
            default: [],
        },

    ],
}

服务器

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    include: [ "syslog/client.shard.cml" ],
    program: {
        runner: "elf",
        binary: "bin/server_bin",
    },
    capabilities: [
        { protocol: "examples.keyvaluestore.supporttrees.Store" },
    ],
    expose: [
        {
            protocol: "examples.keyvaluestore.supporttrees.Store",
            from: "self",
        },
    ],
}

领域

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
{
    children: [
        {
            name: "client",
            url: "#meta/client.cm",
        },
        {
            name: "server",
            url: "#meta/server.cm",
        },
    ],
    offer: [
        // Route the protocol under test from the server to the client.
        {
            protocol: "examples.keyvaluestore.supporttrees.Store",
            from: "#server",
            to: "#client",
        },

        // Route diagnostics support to all children.
        {
            dictionary: "diagnostics",
            from: "parent",
            to: [
                "#client",
                "#server",
            ],
        },
    ],
}

然后,您可以使用任何受支持的语言编写客户端和服务器实现:

Rust

客户端

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    anyhow::{Context as _, Error},
    config::Config,
    fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value},
    fuchsia_component::client::connect_to_protocol,
    std::{thread, time},
};

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    println!("Started");

    // Load the structured config values passed to this component at startup.
    let config = Config::take_from_startup_handle();

    // Use the Component Framework runtime to connect to the newly spun up server component. We wrap
    // our retained client end in a proxy object that lets us asynchronously send `Store` requests
    // across the channel.
    let store = connect_to_protocol::<StoreMarker>()?;
    println!("Outgoing connection enabled");

    // This client's structured config has one parameter, a vector of strings. Each string is the
    // path to a resource file whose filename is a key and whose contents are a value. We iterate
    // over them and try to write each key-value pair to the remote store.
    for key in config.write_items.into_iter() {
        let path = format!("/pkg/data/{}.txt", key);
        let value = std::fs::read_to_string(path.clone())
            .with_context(|| format!("Failed to load {path}"))?;
        let res = store
            .write_item(&Item {
                key: key.clone(),
                value: Some(Box::new(Value::Bytes(value.into_bytes()))),
            })
            .await;
        match res? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // Add nested entries to the key-value store as well. The entries are strings, where the first
    // line is the key of the entry, and each subsequent entry should be a pointer to a text file
    // containing the string value. The name of that text file (without the `.txt` suffix) will
    // serve as the entries key.
    for spec in config.write_nested.into_iter() {
        let mut items = vec![];
        let mut nested_store = NestedStore::default();
        let mut lines = spec.split("\n");
        let key = lines.next().unwrap();

        // For each entry, make a new entry in the `NestedStore` being built.
        for entry in lines {
            let path = format!("/pkg/data/{}.txt", entry);
            let contents = std::fs::read_to_string(path.clone())
                .with_context(|| format!("Failed to load {path}"))?;
            items.push(Some(Box::new(Item {
                key: entry.to_string(),
                value: Some(Box::new(Value::Bytes(contents.into()))),
            })));
        }
        nested_store.items = Some(items);

        // Send the `NestedStore`, represented as a vector of values.
        let res = store
            .write_item(&Item {
                key: key.to_string(),
                value: Some(Box::new(Value::Store(nested_store))),
            })
            .await;
        match res? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // Each entry in this list is a null value in the store.
    for key in config.write_null.into_iter() {
        match store.write_item(&Item { key: key.to_string(), value: None }).await? {
            Ok(_) => println!("WriteItem Success at key: {}", key),
            Err(err) => println!("WriteItem Error: {}", err.into_primitive()),
        }
    }

    // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the
    // referenced bug has been resolved, we can remove the sleep.
    thread::sleep(time::Duration::from_secs(2));
    Ok(())
}

服务器

// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Note: For the clarity of this example, allow code to be unused.
#![allow(dead_code)]

use {
    anyhow::{Context as _, Error},
    fidl_examples_keyvaluestore_supporttrees::{
        Item, StoreRequest, StoreRequestStream, Value, WriteError,
    },
    fuchsia_component::server::ServiceFs,
    futures::prelude::*,
    regex::Regex,
    std::cell::RefCell,
    std::collections::HashMap,
    std::collections::hash_map::Entry,
    std::str::from_utf8,
    std::sync::LazyLock,
};

static KEY_VALIDATION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile")
});

// A representation of a key-value store that can contain an arbitrarily deep nesting of other
// key-value stores.
#[allow(clippy::box_collection, reason = "mass allow for https://fxbug.dev/381896734")]
enum StoreNode {
    Leaf(Option<Vec<u8>>),
    Branch(Box<HashMap<String, StoreNode>>),
}

/// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and
/// writes an entry to it.
fn write_item(
    store: &mut HashMap<String, StoreNode>,
    attempt: Item,
    path: &str,
) -> Result<(), WriteError> {
    // Validate the key.
    if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) {
        println!("Write error: INVALID_KEY, For key: {}", attempt.key);
        return Err(WriteError::InvalidKey);
    }

    // Write to the store, validating that the key did not already exist.
    match store.entry(attempt.key) {
        Entry::Occupied(entry) => {
            println!("Write error: ALREADY_EXISTS, For key: {}", entry.key());
            Err(WriteError::AlreadyExists)
        }
        Entry::Vacant(entry) => {
            let key = format!("{}{}", path, entry.key());
            match attempt.value {
                // Null entries are allowed.
                None => {
                    println!("Wrote value: NONE at key: {}", key);
                    entry.insert(StoreNode::Leaf(None));
                }
                Some(value) => match *value {
                    // If this is a nested store, recursively make a new store to insert at this
                    // position.
                    Value::Store(entry_list) => {
                        // Validate the value - absent stores, items lists with no children, or any
                        // of the elements within that list being empty boxes, are all not allowed.
                        if entry_list.items.is_some() {
                            let items = entry_list.items.unwrap();
                            if !items.is_empty() && items.iter().all(|i| i.is_some()) {
                                let nested_path = format!("{}/", key);
                                let mut nested_store = HashMap::<String, StoreNode>::new();
                                for item in items.into_iter() {
                                    write_item(&mut nested_store, *item.unwrap(), &nested_path)?;
                                }

                                println!("Created branch at key: {}", key);
                                entry.insert(StoreNode::Branch(Box::new(nested_store)));
                                return Ok(());
                            }
                        }

                        println!("Write error: INVALID_VALUE, For key: {}", key);
                        return Err(WriteError::InvalidValue);
                    }

                    // This is a simple leaf node on this branch.
                    Value::Bytes(value) => {
                        // Validate the value.
                        if value.is_empty() {
                            println!("Write error: INVALID_VALUE, For key: {}", key);
                            return Err(WriteError::InvalidValue);
                        }

                        println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap());
                        entry.insert(StoreNode::Leaf(Some(value)));
                    }
                },
            }
            Ok(())
        }
    }
}

/// Creates a new instance of the server. Each server has its own bespoke, per-connection instance
/// of the key-value store.
async fn run_server(stream: StoreRequestStream) -> Result<(), Error> {
    // Create a new in-memory key-value store. The store will live for the lifetime of the
    // connection between the server and this particular client.
    let store = RefCell::new(HashMap::<String, StoreNode>::new());

    // Serve all requests on the protocol sequentially - a new request is not handled until its
    // predecessor has been processed.
    stream
        .map(|result| result.context("failed request"))
        .try_for_each(|request| async {
            // Match based on the method being invoked.
            match request {
                StoreRequest::WriteItem { attempt, responder } => {
                    println!("WriteItem request received");

                    // The `responder` parameter is a special struct that manages the outgoing reply
                    // to this method call. Calling `send` on the responder exactly once will send
                    // the reply.
                    responder
                        .send(write_item(&mut store.borrow_mut(), attempt, ""))
                        .context("error sending reply")?;
                    println!("WriteItem response sent");
                }
                StoreRequest::_UnknownMethod { ordinal, .. } => {
                    println!("Received an unknown method with ordinal {ordinal}");
                }
            }
            Ok(())
        })
        .await
}

// A helper enum that allows us to treat a `Store` service instance as a value.
enum IncomingService {
    Store(StoreRequestStream),
}

#[fuchsia::main]
async fn main() -> Result<(), Error> {
    println!("Started");

    // Add a discoverable instance of our `Store` protocol - this will allow the client to see the
    // server and connect to it.
    let mut fs = ServiceFs::new_local();
    fs.dir("svc").add_fidl_service(IncomingService::Store);
    fs.take_and_serve_directory_handle()?;
    println!("Listening for incoming connections");

    // The maximum number of concurrent clients that may be served by this process.
    const MAX_CONCURRENT: usize = 10;

    // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit.
    fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| {
        run_server(stream).unwrap_or_else(|e| println!("{:?}", e))
    })
    .await;

    Ok(())
}

C++(自然)

客户端

// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.

服务器

// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.

C++(有线)

客户端

// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.

服务器

// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.

fi-0194

fi-0195

fi-0196

fi-0200

fi-0201:未选择平台版本

如果您在编译版本化 FIDL 库时未选择版本,就会发生此错误:

// fidlc --files test.fidl --out test.json
@available(platform="foo", added=1)
library test.bad.fi0201;

如需解决此问题,请使用 --available 命令行标志选择一个版本:

// fidlc --files test.fidl --out test.json --available foo:1
@available(platform="foo", added=1)
library test.good.fi0201;

版本必须是大于或等于 1 的数字,或者是特殊版本 NEXT 和 HEAD 之一。如需了解详情,请参阅 FIDL 版本控制文档。

fi-0202

fi-0203:移除和替换是互斥的

@available 属性支持参数 removed 和 replaced,但它们不能一起使用:

@available(added=1)
library test.bad.fi0203;

protocol Foo {
    @available(removed=2, replaced=2)
    Foo();
};

如需修正此错误,请删除其中一个实参。如果您打算移除该元素而不进行替换,请保留 removed 并删除 replaced:

@available(added=1)
library test.good.fi0203a;

open protocol Foo {
    @available(removed=2)
    strict Foo();
};

或者,如果您要将该元素替换为新定义,请保留 replaced 并删除 removed:

@available(added=1)
library test.good.fi0203b;

open protocol Foo {
    @available(replaced=2)
    strict Foo();

    @available(added=2)
    flexible Foo();
};

同时使用 removed 和 replaced 没有意义,因为它们的含义相反。当某个元素被标记为 removed 时,fidlc 会验证在同一版本中没有添加替换元素。当某个元素被标记为 replaced 时,fidlc 会验证是否在同一版本中添加了替换元素。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0204:无法替换库

@available 属性的 replaced 参数不能用于库声明:

@available(added=1, replaced=2)
library test.bad.fi0204;

请改用 removed 参数:

@available(added=1, removed=2)
library test.good.fi0204;

replaced 实参表示元素被新定义替换。整个库不支持此操作,因为我们假设每个库只有一组定义它的文件。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0205:@available(removed=N) 无效

如果某个元素被标记为 @available(removed=N),则表示该元素无法再在版本 N 中使用。无法重复使用其名称:

@available(added=1)
library test.bad.fi0204;

open protocol Foo {
    @available(removed=2)
    strict Bar();
    @available(added=2)
    flexible Bar();
};

如果您想将该元素替换为新定义(相同的 API 和 ABI),请使用 replaced 参数,而不是 removed 参数:

@available(added=1)
library test.good.fi0204a;

open protocol Foo {
    @available(replaced=2)
    strict Bar();
    @available(added=2)
    flexible Bar();
};

如果您想移除该元素并定义一个新的不相关元素(不同的 API 和 ABI),请为新元素选择一个不同的名称:

@available(added=1)
library test.good.fi0204b;

open protocol Foo {
    @available(removed=2)
    strict Bar();
    @available(added=2)
    flexible NewBar();
};

如果您确实想重复使用名称(相同的 API,不同的 ABI),请使用 renamed 参数在移除后重命名旧元素,从而释放其原始名称:

@available(added=1)
library test.good.fi0204c;

open protocol Foo {
    @available(removed=2, renamed="DeprecatedBar")
    strict Bar();

    @available(added=2)
    @selector("NewBar")
    flexible Bar();
};

请注意,在这种情况下,您必须使用 @selector,以确保新方法具有不同的 ABI。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0206:@available(replaced=N) 无效

如果某个元素被标记为 @available(replaced=N),则表示该元素已被标记为 @available(added=N) 的新定义所取代。如果 FIDL 编译器找不到此类定义,则会报告错误:

@available(added=1)
library test.bad.fi0205;

open protocol Foo {
    @available(replaced=2)
    strict Bar();
};

如果您不打算替换该元素,请使用 removed 实参,而不是 replaced 实参:

@available(added=1)
library test.good.fi0205a;

open protocol Foo {
    @available(removed=2)
    strict Bar();
};

如果您打算替换该元素,请添加替换定义:

@available(added=1)
library test.good.fi0205b;

open protocol Foo {
    @available(replaced=2)
    strict Bar();
    @available(added=2)
    flexible Bar();
};

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0207:类型形状整数溢出

FIDL 类型的大小不得超过 uint32:

library test.bad.fi0207;

type Foo = struct {
    bytes array<uint64, 536870912>;
};

如需修正此错误,请使用较小的数组大小:

library test.good.fi0207;

type Foo = struct {
    bytes array<uint64, 100>;
};

在实践中,FIDL 类型应远小于 232 字节,因为它们通常通过 Zircon 通道发送,而 Zircon 通道限制为每条消息 64 KiB。

fi-0208:预留平台

某些平台名称由 FIDL 预留。例如,“无版本”平台预留用于表示不使用版本控制的库:

@available(platform="unversioned", added=1)
library test.bad.fi0208;

请改用其他平台名称:

@available(platform="foo", added=1)
library test.good.fi0208;

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0209:不允许使用预留字段

FIDL 不再支持 reserved 表或联合字段:

library test.bad.fi0209;

type User = table {
    1: reserved;
    2: email string;
};

保留字段的主要目的是避免意外重复使用序数。有了 FIDL 版本控制,这就不是问题了。您可以为旧字段添加 @available(removed=N) 注解,同时在源文件中保留这些字段(及其序数):

@available(added=1)
library test.good.fi0209a;

type User = table {
    @available(removed=2)
    1: name string;
    2: email string;
};

reserved 的另一个用途是记录序数的预期未来用途。 在这种情况下,请考虑在不稳定的 API 级别 HEAD 定义该字段:

@available(added=1)
library test.good.fi0209b;

type User = table {
    @available(added=HEAD)
    1: name string;
    2: email string;
};

对于 reserved 的任何其他用途,请考虑改为发表评论:

library test.good.fi0209c;

type User = table {
    // We skip ordinal 1 because...
    2: email string;
};

fi-0210: Invalid location in @discoverable client or server argument

可检测到协议的允许 client 或 server 位置必须是 platform 和 external 的逗号分隔列表(可能为空)。换句话说,以下其中一项: - "" - "platform" - "external" - "platform,external" - "external,platform"

library test.bad.fi0210;

@discoverable(server="platform,canada")
protocol Example {};

请确保以下参数正确无误:

library test.good.fi0210;

@discoverable(server="platform")
protocol Example {};

fi-0211: Element cannot be renamed

@available 属性的 renamed 参数只能用于声明的成员,而不能用于声明本身:

@available(added=1)
library test.bad.fi0211;

@available(replaced=2, renamed="Bar")
type Foo = struct {};

@available(added=2)
type Bar = struct {};

请勿重命名声明,而是移除旧声明并添加新声明:

@available(added=1)
library test.good.fi0211;

@available(removed=2)
type Foo = struct {};

@available(added=2)
type Bar = struct {};

只有成员支持重命名,因为 FIDL 编译器可以比较其 ABI 标识(例如表序数),以确保正确完成重命名。

以下元素也不允许使用 renamed 参数:

  • library:您无法从内部重命名库,因为 FIDL 工具链假定每个库都有一个名称。而是应创建一个具有所需名称的新库,并将用户迁移到该库。
  • compose:您无法重命名协议组合,因为组合最初并未命名。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0212: Renamed without replaced or removed

不允许单独使用 @available 参数 renamed。它必须与 replaced 或 removed 参数一起使用:

@available(added=1)
library test.bad.fi0212;

protocol Foo {
    @available(renamed="New")
    Old();
};

如果您只想重命名版本 N 中的元素,请使用 replaced=N 并定义一个替换元素,其中新名称标记为 added=N:

@available(added=1)
library test.good.fi0212a;

protocol Foo {
    @available(replaced=2, renamed="New")
    Old();

    @available(added=2)
    @selector("Old")
    New();
};

在这种情况下,替换方法必须替换 @selector,以实现 ABI 兼容性。

或者,如果您想在版本 N 中移除该元素,并在移除后通过其他名称引用该元素,请使用 removed=N:

@available(added=1)
library test.good.fi0212b;

protocol Foo {
    @available(removed=2, renamed="DeprecatedOld")
    Old();
};

在这种情况下,只有在定位到多个版本(例如 --available test:1,2)时,才会使用新名称,因为这是在定位到移除该元素的版本的同时包含该元素的唯一方法。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0213: Renamed to same name

使用 @available 实参 renamed 重命名元素时,新名称不能与元素的原始名称相同:

@available(added=1)
library test.bad.fi0213;

type Foo = table {
    @available(replaced=2, renamed="bar")
    1: bar string;
    @available(added=2)
    1: bar string:10;
};

如需修正该错误,请移除 renamed 参数:

@available(added=1)
library test.good.fi0213a;

type Foo = table {
    @available(replaced=2)
    1: bar string;
    @available(added=2)
    1: bar string:10;
};

或者,保留 renamed 实参,但选择其他名称:

@available(added=1)
library test.good.fi0213b;

type Foo = table {
    @available(replaced=2, renamed="baz")
    1: bar string;
    @available(added=2)
    1: baz string:10;
};

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0214:@available(removed=N, renamed="NewName") 无效

这类似于 fi-0205:@available(removed=N) 无效,但适用于涉及 renamed 参数的情况。

当某个元素被标记为 @available(removed=N, renamed="NewName") 时,表示该元素在版本 N 中无法再使用,并且在移除后会重命名为“NewName”。您不能将“NewName”用于其他用途:

@available(added=1)
library test.bad.fi0214;

open protocol Foo {
    @available(removed=2, renamed="NewName")
    strict OldName();

    @available(added=2)
    flexible NewName();
};

如果您想在重命名元素的同时保留其 ABI,请使用 replaced 实参,而不是 removed 实参:

@available(added=1)
library test.good.fi0214a;

open protocol Foo {
    @available(replaced=2, renamed="NewName")
    strict OldName();

    @available(added=2)
    @selector("OldName")
    flexible NewName();
};

请注意,在这种情况下,您必须使用 @selector,以确保重命名的方法具有相同的 ABI。

如果您希望新元素具有不同的 ABI,请保留 removed,并确保 renamed 参数和新元素使用不同的名称:

@available(added=1)
library test.good.fi0214b;

open protocol Foo {
    @available(removed=2, renamed="NewName")
    strict OldName();

    @available(added=2)
    strict DifferentName();
};

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0215:@available(replaced=N, renamed="NewName") 无效

这类似于 fi-0206:@available(replaced=N) 无效,但适用于涉及 renamed 参数的情况。

当某个元素被标记为 @available(replaced=N, renamed="NewName") 时,表示该元素已被名为“NewName”且标记为 @available(added=N) 的新定义所取代。如果 FIDL 编译器找不到此类定义,则会报告错误:

@available(added=1)
library test.bad.fi0215;

open protocol Foo {
    @available(replaced=2, renamed="NewName")
    strict OldName();
};

如需修正此错误,请使用新名称定义一个元素:

@available(added=1)
library test.good.fi0215;

open protocol Foo {
    @available(replaced=2, renamed="NewName")
    strict OldName();

    @available(added=2)
    @selector("OldName")
    strict NewName();
};

请注意,您必须使用 @selector,以确保重命名的方法具有相同的 ABI。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0216:@available(removed=N) (ABI) 无效

这类似于 fi-0205:@available(removed=N) 无效,但不是重复使用元素的名称,而是其 ABI:

@available(added=1)
library test.bad.fi0216;

open protocol Foo {
    @available(removed=2)
    strict Bar();

    @available(added=2)
    @selector("Bar")
    flexible Qux();
};

如果您有意替换元素的 ABI,请使用参数 replaced 和 renamed(而非 removed)明确指定:

@available(added=1)
library test.good.fi0216a;

open protocol Foo {
    @available(replaced=2, renamed="Qux")
    strict Bar();

    @available(added=2)
    @selector("Bar")
    flexible Qux();
};

如果您不打算重复使用该 ABI,请选择其他 ABI。在这种情况下,我们可以移除 @selector 属性,并根据方法的名称使用其默认选择器:

@available(added=1)
library test.good.fi0216b;

open protocol Foo {
    @available(removed=2)
    strict Bar();

    @available(added=2)
    flexible Qux();
};

此错误可能发生在其他成员(而不仅仅是方法)上。对于位和枚举成员,ABI 是整数值。对于结构体成员,ABI 是字节偏移量。对于表和联合成员,ABI 是序数。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0217:@available(replaced=N) (ABI) 无效

这类似于 fi-0206:@available(replaced=N) 无效,但仅在找到与元素名称(而非其 ABI)匹配的替换项时适用:

@available(added=1)
library test.bad.fi0217;

open protocol Foo {
    @available(replaced=2)
    strict Bar();

    @available(added=2)
    @selector("NotBar")
    flexible Bar();
};

如果您打算替换该元素,请确保其 ABI 匹配。在这种情况下,我们可以移除 @selector 属性,因为这两种方法已经具有相同的名称:

@available(added=1)
library test.good.fi0217a;

open protocol Foo {
    @available(replaced=2)
    strict Bar();

    @available(added=2)
    flexible Bar();
};

如果您不打算替换 ABI,请使用 removed,而不是 replaced。 在这种情况下,我们还必须选择一个不同的名称,以免与旧名称冲突:

@available(added=1)
library test.good.fi0217b;

open protocol Foo {
    @available(removed=2)
    strict Bar();

    @available(added=2)
    flexible NotBar();
};

如果您确实想重复使用名称,但不想重复使用 ABI,请使用 removed 而不是 replaced,并使用 renamed 在移除后重命名旧元素,从而释放其原始名称:

@available(added=1)
library test.good.fi0217c;

open protocol Foo {
    @available(removed=2, renamed="DeprecatedBar")
    strict Bar();

    @available(added=2)
    @selector("NotBar")
    flexible Bar();
};

此错误可能会发生在其他成员(而不仅仅是方法)上。对于位和枚举成员,ABI 是整数值。对于结构体成员,ABI 是字节偏移量。对于表和联合成员,ABI 是序数。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0218:修饰符可用性参数无效

在 FIDL 版本控制修饰符语法中,您只能使用参数 added 和 removed。不允许使用其他 @available 参数,例如 deprecated:

@available(added=1)
library test.bad.fi0218;

type Foo = resource(deprecated=2) struct {};

如需修正此错误,请移除不受支持的实参:

@available(added=1)
library test.good.fi0218;

type Foo = resource struct {};

与声明和成员不同,修饰符没有自己的生命周期,因此弃用、替换和重命名等概念对它们没有意义。修饰符只能添加和移除。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0219: Cannot change method strictness

借助 FIDL 版本控制修饰符语法,您可以添加或移除 strict 和 flexible 修饰符。不过,如果双向方法不使用 error 语法,则不允许这样做,因为此类更改会破坏 ABI:

@available(added=1)
library test.bad.fi0219;

open protocol Foo {
    strict(removed=2) flexible(added=2) Method() -> ();
};

而是应移除严格方法,并添加一个具有不同名称的灵活方法来替换它:

@available(added=1)
library test.good.fi0219a;

open protocol Foo {
    @available(removed=2)
    strict Method() -> ();

    @available(added=2)
    flexible NewMethod() -> ();
};

或者,如果您想为新 ABI 重复使用方法名称,可以使用 renamed 参数和 @selector 属性:

@available(added=1)
library test.good.fi0219b;

open protocol Foo {
    @available(removed=2, renamed="StrictMethod")
    strict Method() -> ();

    @available(added=2)
    @selector("FlexibleMethod")
    flexible Method() -> ();
};

不允许在没有错误语法的情况下更改双向方法的严格性,因为这会改变响应的形式。当双向方法灵活或使用错误语法时,FIDL 会自动生成一个封装响应的结果联合。因此,只有对于具有错误语法的双向方法,更改严格性才是安全的。

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0220:在版本范围内未找到名称

当在特定版本中找不到名称,但在其他版本中找到该名称时,就会发生此错误。例如,在版本 1 中添加的元素 Foo 无法引用在版本 2 中添加的另一个元素 Bar,因为 Bar 在版本 1 中不存在:

@available(added=1)
library test.bad.fi0220;

alias Foo = Bar;

@available(added=2)
type Bar = struct {};

在这种情况下,我们可以通过添加 @available 属性来修正错误,使 Foo 也在版本 2 中添加:

@available(added=1)
library test.good.fi0220;

@available(added=2)
alias Foo = Bar;

@available(added=2)
type Bar = struct {};

如需详细了解版本控制,请参阅 FIDL 版本控制。

fi-0221:resource 出现在带注解 @no_resource 的声明中

当声明具有 @no_resource 属性,但其中的某些项具有 resource 修饰符时,就会发生此错误。

library test.bad.fi0221;

@no_resource
open protocol Foo {
    flexible Bar(resource struct {
        a uint8;
    });
};

您可以通过移除属性或移除资源修饰符来修正错误。

library test.good.fi0221;

@no_resource
open protocol Foo {
    flexible Bar(struct {
        a uint8;
    });
};

fi-0222:@no_resource 是实验性属性

@no_resource 注解处于实验阶段,仅供少数特定协议使用。

library test.bad.fi0222;

@no_resource
type Foo = struct {
    a uint32;
};

该注解会激活新的编译器错误,但对语义没有任何影响。直接移除应该没问题。

library test.good.fi0222;

type Foo = struct {
    a uint32;
};

fi-0223:协议具有 @no_resource 属性,因此无法组合其他协议

如果带有 @no_resource 注解的协议组合了未带有该注解的另一个协议,则会发生此错误。

library test.bad.fi0223;

open protocol A {
    flexible F();
};

@no_resource
open protocol B {
    compose A;
    flexible G();
};

如需解决此问题,请将 @no_resource 添加到组合协议中。

library test.good.fi0223;

@no_resource
open protocol A {
    flexible F();
};

@no_resource
open protocol B {
    compose A;
    flexible G();
};

fi-0224:不能为非零值

当您尝试将某个类型标记为非零,但该类型不能为非零时,就会发生此错误。

library test.bad.fi0224;

type Bar = struct {
    foo float32:nonzero;
};

如需修正此错误,请移除“非零”限制条件:

library test.good.fi0224;

type Bar = struct {
    foo float32;
};

只有整数 FIDL 类型可以限制为非零值。

fi-0225:类型不能两次标记为非零

当某个类型两次被设为非零时,会发生此错误。通常,当类型在其使用和声明位置都被标记为非零时,就会发生这种情况。

library test.bad.fi0225;

alias MyAlias = int32:nonzero;

type MyStruct = struct {
    my_member MyAlias:nonzero;
};

如需修正此错误,请仅将类型设置为非零一次。

例如,您可以从使用网站中移除 :nonzero。

library test.good.fi0225a;

alias MyAlias = int32:nonzero;

type MyStruct = struct {
    my_member MyAlias;
};

您也可以从别名声明中移除 :nonzero。

library test.good.fi0225b;

alias MyAlias = int32;

type MyStruct = struct {
    my_member MyAlias:nonzero;
};

fi-0226:非零常量的值无效

如果常量值的基础类型被限制为非零值,则常量值不能为零:

library test.bad.fi0226;

const FOO uint8:nonzero = 0;
const BAR int32:nonzero = 0;

您可以通过将值更改为类型范围内的值来解决此问题:

library test.good.fi0226a;

const FOO uint8:nonzero = 1;
const BAR int32:nonzero = 4;

或者,通过更改类型来适应零值:

library test.good.fi0226b;

const FOO uint8 = 0;
const BAR int32 = 0;