💯

cargo-componentによって生成されるbindingsのモジュールネーミングルール

2023/12/23に公開

cargo-component is 何?

cargo-componentはWASM Component Model仕様に準拠したWASMをビルドするためのツールです。Rustで最も基本的なツールであるCargoのサブコマンドという形で実装されているのでCargoに慣れた人にとっては学習コストを低く抑えられることが特徴です。
https://github.com/bytecodealliance/cargo-component

※WASM Compoinet Modelは現在進行系で策定が進んでいるWASMの仕様で、こちらの記事でオーバービューを紹介しているので良かったら参照してください。
https://zenn.dev/newgyu/articles/82a181212c7bb2

bindingsの生成

bindingsはcargo_component_bindings::generateマクロによってWITファイルの情報を元に生成されます。以下のコードはcomponent-modelのチュートリアルからの抜粋です。

https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/calculator/src/lib.rs

これらのbindingsから始まるモジュールに関連するコードはマクロが生成したもので、開発者としてはGuest traitや、Op列挙型のコードは書いていません。

use bindings::exports::docs::calculator::calculate::{Guest, Op};
use bindings::docs::calculator::add::add;

生成結果から推測される規則

calculatorコンポーネントは以下のWITで定義されています。

package docs:calculator@0.1.0;

interface calculate {
    enum op {
        add,
    }
    eval-expression: func(op: op, x: u32, y: u32) -> u32;
}

interface add {
    add: func(a: u32, b: u32) -> u32;
}

world calculator {
    export calculate;
    import add;
}

ここから以下の推測ができます。

use bindings::exports::docs::calculator::calculate::{Guest, Op};
module name 意味
bindings::exports exportされるものにつく接頭辞
::docs::calculator パッケージ名
::calculate インターフェース名
  • exportするinterfaceは常にGuestという名前のtraitとしてbindingが生成されるようです
  • OpはWITに定義されているenum opがRustの列挙型としてbindingが生成されています
use bindings::docs::calculator::add::add;
module name 意味
bindings importされるものはexportsを含まない
::docs::calculator パッケージ名
::add インターフェース名

最後のaddinterface addの中にあるfunc addですね。

そうはならないケースがある

チュートリアルから逸脱して自分でWITを書いてみました。
https://github.com/NewGyu/wasm-component-example/blob/compare/wit-gen/case1/wit/world.wit

チュートリアルのケースではworldがimport/exportするのはinterfaceでしたが、上記の例ではfuncをexportしています。

このWITを元に生成されたbindingsは以下のとおりです。
https://github.com/NewGyu/wasm-component-example/blob/compare/wit-gen/case1/src/lib.rs

  • exportされたfunc randを表すGuest traitはbindingsモジュール直下
    --> bindings::exports::docs::calculator::Guestにはならないのか?
  • useしているrandomインターフェースの中にあるrecordとenumは上記のimportの規則に従っているように見える

どういうこと?

質問してみた

と、いうわけでcargo-componentのissueとして聞いてみました。
https://github.com/bytecodealliance/cargo-component/issues/199

メインコミッターであるPeter氏は私のようなドシロウトに非常に丁寧に答えてくれる方で、とてもありがたいです。

彼の回答からわかったのは以下のことでした。

  • 基本的にはこのルールはwit-bindgenの仕様に基づくものである
  • cargo-componentのマクロはそれに接頭辞としてbindingsをつける
  • worldから関数が直接exportされる場合にはGuest traitはbindingsモジュール直下につくられる
  • worldからinterfaceがexportされる場合には私の推測通りbindings::exports::パッケージ名::インターフェース名というモジュール以下にGuest traitが作られる
  • worldからimportされるものは推測通りbindings::パッケージ名::インターフェース名モジュールになる

これでスッキリしたクリスマスを迎えられそうです!

Discussion