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 類型轉換為 Go 中的原生類型。這些類型可以顯示為匯總類型的成員,或做為通訊協定方法的參數。

內建類型

系統會根據下表將 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:S,zx.Handle:<S, optional> 如果 Go 執行階段支援,則會使用對等的帳號代碼類型 (例如 zx.VMOzx.Channelzx.Event)。否則,系統會使用 zx.Handle
zx.Handle,zx.Handle:optional zx.Handle

使用者定義的類型

在 Go 中,使用者定義的類型 (位元、列舉、常數、結構、聯集或資料表) 是指使用產生的類型 (請參閱「類型定義」)。使用者定義類型 T 的可為空值版本參照產生的類型為 *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 位元傳回 0。
  • 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:傳回這個列舉值是否為未知值。如果是 strict 列舉,一律會傳回 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: reserved;
    2: int_value int32;
    3: string_value string:MAX_STRING_LENGTH;
};

FIDL 會產生代表聯集標記的別名和相關常數:

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:傳回未知資料的原始位元組和控點。控點片段會按照遍歷順序傳回,如果聯集是資源類型,則處理方保證為空白。

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

為含有不明變體的聯集編碼會將未知的資料,將原始序數寫回線路。

解碼不明變化版本時,Strict 聯集會失敗。使用帳號代碼解碼不明變數時,類型的彈性聯集會失敗。

桌子

以下列資料表定義為例:

type User = table {
    1: reserved;
    2: age uint8;
    3: 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:欄位 getter,如果不存在,就會傳回指定的預設值。
  • func (u *User) ClearAge()func (u *User) ClearName():清除欄位的存在。
  • func (u *User) HasUnknownData() bool:檢查是否有任何未知欄位。
  • func (u *User) GetUnknownData() map[uint64]fidl.UnknownData:傳回位元組對位元組的對應,並處理任何不明欄位。帳號代碼清單會按照遍歷順序傳回;如果資料表是類型,則字串保證為空白。

使用範例:

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 保留的名稱做為內嵌版面配置。

通訊協定

指定「通訊協定」

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 介面,用戶端會在 Proxy 呼叫伺服器時使用,以及伺服器實作通訊協定:

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。相關說明請參閱「用戶端」和「伺服器」一節。

用戶端

用來透過 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 會產生為包含兩個變化版本的聯集ErrMoveErrorMoveErrorResponse,則為 TicTacToeMakeMoveResponse

TicTacToeMakeMoveResponse 會產生為 struct,其中包含對應成功回應參數的欄位。在本例中,包含 GameState 類型的單一 NewState 欄位。

通訊協定組成

FIDL 沒有繼承的概念,而是針對所有組合的通訊協定產生上述完整程式碼。換句話說,產生的程式碼

protocol A {
    Foo();
};

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

為下列項目提供與程式碼產生相同的 API:

protocol A {
    Foo();
};

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

除了方法序數之外,產生的程式碼皆相同。

通訊協定和方法屬性

過渡

為了支援 Go 中的 @transitional 屬性,FIDL 會產生 TicTacToeWithCtxTransitionalBase 類型,為所有標示為 @transitional 的方法提供預設實作。嵌入 TicTacToeWithCtxTransitionalBase 的伺服器實作會繼續新增一個轉換方法。

可供偵測

標示為 @discoverable 時,產生的 InterfaceRequest 類型 (在此範例中為 TicTacToeWithCtxInterfaceRequest) 會實作 fidl.ServiceRequest,以便讓伺服器端在服務探索中使用。

此外,FIDL 會產生包含通訊協定名稱的 TicTacToeName 常數。