Go 绑定

对于 library 声明:

library fuchsia.examples;

绑定代码会生成到 examples Go 软件包中,后者会由 采用 FIDL 库名称的最后一个组成部分。

可使用以下路径导入软件包:

import "fidl/fuchsia/examples"

常量

常量会以 const 块的形式生成。例如, 以下常量:

const BOARD_SIZE uint8 = 9;
const NAME string = "Tic-Tac-Toe";

生成为:

const (
  BoardSize uint8  = 9
  Name      string = "Tic-Tac-Toe"
)

有关 FIDL 基元类型与 Go 类型之间的对应关系,请参见 内置类型

字段

本部分介绍 FIDL 工具链如何将 FIDL 类型转换为原生 类型。这些类型可以在汇总类型中显示为成员,也可以显示为 传递给协议方法。

内置类型

系统会根据下表将 FIDL 类型转换为 Go 类型。

FIDL 类型 Go 类型
bool bool
int8 int8
int16 int16
int32 int32
int64 int64
uint8 uint8
uint16 uint16
uint32 uint32
uint64 uint64
float32 float32
float64 float64
array<T, N> [N]T
vector<T>:N []T
vector<T>:N? *[]T
string string
string:optional *string
server_end:P 生成的服务器端类型 PInterfaceRequest,请参阅协议
client_end:P 生成的客户端类型 PInterface,请参阅协议
zx.Handle:Szx.Handle:<S, optional> 如果 Go 运行时支持等效句柄类型(例如 zx.VMOzx.Channelzx.Event),则使用等效句柄类型。否则,使用 zx.Handle
zx.Handlezx.Handle:optional zx.Handle

用户定义的类型

在 Go 中,用户定义的类型(位、枚举、常量、结构体、联合或表) (请参阅类型定义)。 用户定义的类型 T 的可为 null 版本使用指针进行引用 更改为生成的类型:*T

类型定义

请注意,在本部分中,生成的 Go 代码示例并非 由 FIDL 生成的确切代码。例如,生成的结构体 包含无法通过反射进行检查的非导出字段。

块数

根据 bits 的定义:

const BOARD_SIZE uint8 = 9;
const NAME string = "Tic-Tac-Toe";

FIDL 为底层类型生成类型别名(否则为 uint32) 和常量:

type FileMode uint16

const (
  FileModeRead    FileMode = 1
  FileModeWrite   FileMode = 2
  FileModeExecute FileMode = 4
  FileMode_Mask   FileMode = 7
)

FileMode_Mask 值是一个位掩码,其中包含定义的每个位成员 使用 FIDL 架构。

此外,它还为 FileMode 提供了以下方法:

  • func (x FileMode) String() string:返回 位。
  • func (x FileMode) GetUnknownBits() uint64:返回仅包含 此位值中的未知成员,以 uint64 表示。对于 strict 位。
  • func (x FileMode) HasUnknownBits() bool:返回此值是否包含 任何未知位。对于严格位,始终返回 false
  • func (x FileMode) InvertBits() FileMode:反转所有已知位。全部 则设为 false。
  • func (x FileMode) ClearBits(mask FileMode) FileMode:修改位字段 以使掩码中设置的所有位均未设置。
  • func (x FileMode) HasBits(mask FileMode) bool:验证是否已全部翻转 设置掩码中的位。

用法示例:

func ExampleFileMode() {
	fmt.Println(examples.FileModeRead.String())
	fmt.Println(examples.FileModeWrite | examples.FileModeExecute)

	// Output:
	// Read
	// Write|Execute
}

枚举

根据 enum 定义:

type LocationType = strict enum {
    MUSEUM = 1;
    AIRPORT = 2;
    RESTAURANT = 3;
};

FIDL 为底层类型生成类型别名(否则为 uint32) 和常量:

type LocationType uint32

const (
  LocationTypeMuseum     LocationType = 1
  LocationTypeAirport    LocationType = 2
  LocationTypeRestaurant LocationType = 3
)

如果 LocationType灵活,则其将具有未知 占位符成员:

    LocationType_Unknown LocationType = 0x7fffffff

如果枚举具有使用 [Unknown] 属性标记的成员, 生成的未知变量的值与标记的未知变量的值相同 成员。

LocationType 提供以下方法:

  • func (x LocationType) IsUnknown() bool:返回此枚举值是否 未知。对于严格枚举,始终返回 false
  • func (x LocationType) String() string:返回人类可读字符串 枚举。

用法示例:

func ExampleLocationType() {
	fmt.Println(examples.LocationTypeMuseum.String())
	// Output: Museum
}

结构体

对于 struct 声明:

type Color = struct {
    id uint32;
    @allow_deprecated_struct_defaults
    name string:MAX_STRING_LENGTH = "red";
};

FIDL 工具链会生成包含匹配字段的 Color 结构体:

type Color struct {
  Id   uint32
  Name string
}

Go 绑定目前不支持结构体字段的默认值。

用法示例:

func ExampleColor() {
	red := examples.Color{Id: 1, Name: "ruby"}
	fmt.Println(red.Id)
	fmt.Println(red.Name)

	// Output:
	// 1
	// ruby
}

联合体

根据并集的定义:

type JsonValue = strict union {
    1: int_value int32;
    2: string_value string:MAX_STRING_LENGTH;
};

FIDL 会生成别名和表示 union tag

type I_jsonValueTag uint64

const (
  JsonValueIntValue     = 2
  JsonValueStringValue  = 3
)

还有一个包含标记字段的 JsonValue 结构体, 联合的变体

type JsonValue struct {
  I_jsonValueTag
  IntValue       int32
  StringValue    string
}

JsonValue 提供以下方法:

  • func (_m *JsonValue) Which() I_jsonValueTag:返回联合标记。
  • func (_m *JsonValue) SetIntValue(intValue int32)func (_m *JsonValue) SetStringValue(stringValue string):设置联合以包含特定的 变体,对代码进行相应的更新。

如果 JsonValue灵活,则以下规则 其他方法:

  • func (_m *JsonValue) GetUnknownData() fidl.UnknownData:返回原始 未知数据的字节和句柄。句柄 Slice 在 遍历顺序,如果并集是空的,则该函数必须为空 一种资源类型。

FIDL 工具链还会生成用于构建实例的工厂函数 (共 JsonValue 个):

  • func JsonValueWithIntValue(intValue int32) JsonValue
  • func JsonValueWithStringValue(stringValue string) JsonValue

用法示例:

func ExampleJsonValue() {
	val := examples.JsonValueWithStringValue("hi")
	fmt.Println(val.Which() == examples.JsonValueStringValue)
	fmt.Println(val.StringValue)
	val.SetIntValue(1)
	fmt.Println(val.Which() == examples.JsonValueIntValue)
	fmt.Println(val.IntValue)

	// Output:
	// true
	// hi
	// true
	// 1
}

灵活并集和未知变体

灵活联合在生成的代码中有一个额外的变体 类:

const (
  JsonValue_unknownData = 0
  // other tags omitted...
)

当包含未知变体的并集的 FIDL 消息被解码为 JsonValue.Which() 将返回 JsonValue_unknownData

对包含未知变体的并集进行编码会写入未知数据和 重新连接到电线上。

在解码未知变体时,严格联合会失败。 在以下情况下,属于类型的灵活联合会失败 使用句柄对未知变体进行解码。

表格

基于以下定义:

type User = table {
    1: age uint8;
    2: name string:MAX_STRING_LENGTH;
};

FIDL 工具链会生成一个 User 结构体,其中每个结构体都有在线状态字段 字段:

type User struct {
  Age         uint8
  AgePresent  bool
  Name        string
  NamePresent bool
}

User 提供以下方法:

  • func (u *User) HasAge() boolfunc (u *User) HasName() bool:检查 某个字段的存在
  • func (u *User) SetAge(age uint8)func (u *User) SetName(name string): 字段 setter。
  • func (u *User) GetAge() uint8func (u *User) GetName() string:字段 getter 方法。
  • func (u *User) GetAgeWithDefault(_default uint8) uint8func (u *User) GetNameWithDefault(_default string) string:返回 则使用指定的默认值。
  • func (u *User) ClearAge()func (u *User) ClearName():清除 字段。
  • func (u *User) HasUnknownData() bool:检查是否存在任何未知 字段。
  • func (u *User) GetUnknownData() map[uint64]fidl.UnknownData:返回地图 从序数到字节和句柄。标识名列表 按遍历顺序返回,如果存在以下情况,则保证为空 该表是 value 类型。

用法示例:

func ExampleUser() {
	var user examples.User
	fmt.Println(user.HasAge(), user.HasName())
	user.SetAge(30)
	user.SetName("John")
	fmt.Println(user.GetAge(), user.GetName())
	user.ClearAge()
	user.ClearName()
	fmt.Println(user.HasAge(), user.HasName())
	fmt.Println(user.GetNameWithDefault("Unknown"))

	// Output:
	// false false
	// 30 John
	// false false
	// Unknown
}

内嵌布局

生成的 Go 代码使用 fidlc 预留的名称 内嵌布局

协议

给定 protocol

closed protocol TicTacToe {
    strict StartGame(struct {
        start_first bool;
    });
    strict MakeMove(struct {
        row uint8;
        col uint8;
    }) -> (struct {
        success bool;
        new_state box<GameState>;
    });
    strict -> OnOpponentMove(struct {
        new_state GameState;
    });
};

FIDL 生成一个 TicTacToeWithCtx 接口,在以下情况下,客户端会使用该接口: 以及由服务器代理调用服务器来实现协议:

type TicTacToeWithCtx interface {
  StartGame(ctx_ fidl.Context, startFirst bool) error
  MakeMove(ctx_ fidl.Context, row uint8, col uint8) (bool, *GameState, error)
}

每个方法都将 Context 作为第一个参数,后跟请求 参数。触发和忘记方法会返回 error,双向方法会返回 响应参数,后跟 error

TicTacToe 协议交互的入口点如下 函数:

func NewTicTacToeWithCtxInterfaceRequest() (TicTacToeWithCtxInterfaceRequest, *TicTacToeWithCtxInterface, error)

此函数会创建一个通道并返回 TicTacToeWithCtxInterfaceRequest。 绑定到通道一端(表示服务器端), TicTacToeWithCtxInterface,绑定到另一端,代表客户端 end 的值。客户端服务器部分对这些操作进行了说明 部分。

客户端

用于通过 TicTacToe 协议通信的通道的客户端一端 为 TicTacToeWithCtxInterface。它会实现 TicTacToeWithCtx 协议中介绍的接口以及处理方法 事件。请注意,在此实现中,双向方法调用 同步和阻塞,直到收到响应。

如需查看 Go FIDL 客户端的示例,请访问 //examples/fidl/go/client

服务器

为此 FIDL 协议实现服务器涉及提供一个具体的 TicTacToeWithCtx 接口的实现。

绑定生成一个 TicTacToeWithCtxInterfaceRequest 类型,用于 表示通过 TicTacToe 通信的通道的服务器端 协议。它提供以下方法:

  • func (c EchoWithCtxInterfaceRequest) ToChannel() zx.Channel:将 接口请求传回非类型化渠道。

如需查看 Go FIDL 服务器的示例,请访问 //examples/fidl/go/server

事件

客户端

TicTacToeWithCtxInterface 提供了用于处理事件的方法:

  • func (p *TicTacToeWithCtxInterface) ExpectOnOpponentMove(ctx_ fidl.Context) (GameState, error)OnOppponentMove 的事件处理脚本,它接受 Context 并返回事件参数。

调用任意一个事件处理脚本方法,将读取下一个缓冲事件或 阻塞,直到收到事件缓冲区 - 调用方应排空事件缓冲区 及时避免 FIDL 绑定中的无界限缓冲。如果下一个事件 与调用的方法匹配,则返回其参数。否则, 错误。客户需确保订单收到 与已处理事件的顺序匹配。

服务器

服务器可以使用 TicTacToeEventProxy 发送事件,它提供了 方法:

  • func (p *TicTacToeEventProxy) OnOpponentMove(newState GameState) error:发送 OnOpponentMove 事件。

创建 TicTacToeEventProxy 需要访问客户端的通道。 Go 服务器示例展示了如何获取 EventProxy

结果

对于具有错误类型的方法,Go 绑定没有任何特殊处理。

假设该方法带有一个错误类型:

protocol TicTacToe {
    MakeMove(struct {
      row uint8;
      col uint8;
    }) -> (struct {
      new_state GameState;
    }) error MoveError;
};

TicTacToeWithCtx 上的 MakeMove 的方法签名 接口是:

MakeMove(ctx_ fidl.Context, row uint8, col uint8) (TicTacToeMakeMoveResult, error)

TicTacToeMakeMoveResult 以具有两个变体的并集的形式生成: Err,属于 MoveErrorResponse,属于 TicTacToeMakeMoveResponse

TicTacToeMakeMoveResponse 以包含字段的结构体的形式生成 对应成功响应的参数。在本示例中,它包含一个 单个 NewState 字段(类型为 GameState)。

协议组合

FIDL 没有继承的概念,它会生成完整的代码, (针对所有组合协议)执行上述操作。在 也就是

protocol A {
    Foo();
};

protocol B {
    compose A;
    Bar();
};

提供与针对以下各项生成的代码相同的 API:

protocol A {
    Foo();
};

protocol B {
    Foo();
    Bar();
};

除了方法序数之外,生成的代码完全相同。

协议和方法属性

过渡风格

为了在 Go 中支持 @transitional 属性,FIDL 生成了一个 TicTacToeWithCtxTransitionalBase 类型,该类型提供默认实现 每个标记为 @transitional 的方法。嵌入 TicTacToeWithCtxTransitionalBase将继续构建新的 Transitional 方法。

可检测到

标记为 @discoverable 时,生成的 InterfaceRequest 类型(在此例中) 示例 TicTacToeWithCtxInterfaceRequest)会实现 fidl.ServiceRequest, 这允许在服务发现中使用服务器端。

此外,FIDL 还会生成一个 TicTacToeName 常量,其中包含 协议名称。