MoonBit 与组件模型#

本指南演示了如何使用 MoonBit 构建 WebAssembly 组件,利用 WIT(WebAssembly 接口类型)进行接口定义,并使用 wit-bindgen 工具链进行代码生成。

本教程将演示如何构建一个实现 docs:adder 包中定义的 [adder world][adder-wit] 的组件。该组件将导出一个包含两个数字求和的 add 函数的 add 接口。

1. Install the Tools#

安装 MoonBit#

首先,安装 MoonBit 编译器和工具链。请按照 MoonBit 下载页面 的说明进行操作。

验证你的 MoonBit 安装(以下是撰写本文时的版本):

$ moon version --all
moon 0.1.20250826 (8ab6c9e 2025-08-26) ~/.moon/bin/moon
moonc v0.6.25+d6913262c (2025-08-27) ~/.moon/bin/moonc
moonrun 0.1.20250826 (8ab6c9e 2025-08-26) ~/.moon/bin/moonrun
moon-pilot 0.0.1-95f12db ~/.moon/bin/moon-pilot

安装 Wasm 工具链#

  1. 安装 wit-bindgen CLI 工具,它可以从 WIT 文件生成 MoonBit 绑定:

    $ cargo install wit-bindgen-cli
    
  2. 为了使用 WebAssembly 组件,安装 wasm-tools

    $ cargo install wasm-tools
    

验证安装(以下是撰写本文时的版本):

$ wit-bindgen --version
wit-bindgen-cli 0.45.0
$ wasm-tools --version
wasm-tools 1.238.0

2. Define the Interface (WIT)#

在生成 MoonBit 项目之前,你需要使用 WIT 定义组件接口。为你的项目创建一个目录并定义 WIT 文件:

$ mkdir moonbit-adder && cd moonbit-adder
$ mkdir wit

创建 wit/world.wit,内容如下:

package docs:adder@0.1.0;

interface add {
    add: func(x: u32, y: u32) -> u32;
}

world adder {
    export add;
}

此 WIT 定义:

  • 声明一个包 docs:adder,版本为 0.1.0

  • 定义一个 add 接口,包含一个接受两个 u32 参数并返回一个 u32 的函数

  • 创建一个 adder 世界,导出 add 接口

3. Generate MoonBit Project Structure#

使用 wit-bindgen 生成 MoonBit 项目结构和绑定:

$ wit-bindgen moonbit wit/world.wit --out-dir . \
    --derive-eq \
    --derive-show \
    --derive-error

此命令生成以下目录结构:

.
├── ffi
│   ├── moon.pkg.json
│   └── top.mbt
├── gen
│   ├── ffi.mbt
│   ├── gen_interface_docs_adder_add_export.mbt
│   ├── interface
│   │   └── docs
│   │       └── adder
│   │           └── add
│   │               ├── moon.pkg.json
│   │               ├── stub.mbt
│   │               └── top.mbt
│   ├── moon.pkg.json
│   ├── world
│   │   └── adder
│   │       ├── moon.pkg.json
│   │       └── stub.mbt
│   └── world_adder_export.mbt
├── moon.mod.json
├── wit
│   └── world.wit
└── world
    └── adder
        ├── ffi_import.mbt
        ├── import.mbt
        ├── moon.pkg.json
        └── top.mbt

生成的文件包括:

  • moon.mod.json: MoonBit 模块配置

  • gen/: 生成的导出绑定

    • interface/: 生成的导出接口绑定

    • world/: 生成的导出世界绑定

    • stub.mbt: 主实现文件

  • interface/: 生成的导入接口绑定

  • world/: 生成的导入世界绑定

4. Examine the Generated Code#

wit-bindgen 工具生成了处理 WebAssembly 组件接口的 MoonBit 绑定。让我们检查生成的 gen/interface/docs/adder/add/stub.mbt

// Generated by `wit-bindgen` 0.45.0.

pub fn add(_x : UInt, _y : UInt) -> UInt {
      ...
}

... 是 MoonBit 中的占位符语法。当执行 moon check --target wasm 时,会出现“未完成代码”警告。

5. Implement the Component Logic#

现在在 gen/interface/docs/adder/add/stub.mbt 中实现 add 函数:

// Generated by `wit-bindgen` 0.45.0.

///|
pub fn add(x : UInt, y : UInt) -> UInt {
  x + y
}

6. Configure the Build#

确保你的 gen/moon.pkg.json 已正确配置为 WebAssembly 目标:

{
  // link configuration for Wasm backend
  "link": {
    "wasm": {
      "exports": [
        // Export for cabi_realloc
        "cabi_realloc:cabi_realloc",
        // Export per the interface definition
        "wasmExportAdd:docs:adder/add@0.1.0#add"
      ],
      "export-memory-name": "memory",
      "heap-start-address": 16
    }
  },
  "import": [
    {
      "path": "docs/adder/ffi",
      "alias": "ffi"
    },
    {
      "path": "docs/adder/gen/interface/docs/adder/add",
      "alias": "add"
    }
  ]
}

7. Build the WebAssembly Component#

将 MoonBit 代码构建为 WebAssembly:

$ moon build --target wasm

这将生成一个 WebAssembly 模块。为了创建一个合适的 WebAssembly 组件,使用 wasm-tools

$ wasm-tools component embed wit target/wasm/release/build/gen/gen.wasm \
    --encoding utf16 \
    --output adder.wasm
$ wasm-tools component new adder.wasm --output adder.component.wasm

你可以使用 wasm-tools 验证组件的接口:

$ wasm-tools component wit adder.component.wasm

期待的两个命令输出:

package root:component;

world root {
  export docs:adder/add@0.1.0;
}
package docs:adder@0.1.0 {
  interface add {
    add: func(x: u32, y: u32) -> u32;
  }
}

8. Testing the Component#

使用示例宿主#

要测试你的组件,请使用本存储库中提供的 [example-host][example-host]:

$ git clone https://github.com/bytecodealliance/component-docs.git
$ cd component-docs/component-model/examples/example-host
$ cp /path/to/adder.component.wasm .
$ cargo run --release -- 5 3 adder.component.wasm

期待的输出:

5 + 3 = 8

使用 Wasmtime#

你也可以直接使用 wasmtime 测试组件:

$ wasmtime run --invoke 'add(10, 20)' adder.component.wasm
30

9. Configurations#

–derive-eq –derive-show#

这两个选项将为所有生成的类型添加 derive(Eq) 和 / 或 derive(Show)

–derive-error#

此选项将生成名称中包含“Error”的变体/枚举作为 suberrors。这使你能够更轻松地集成 MoonBit 的错误处理。

例如,对于以下接口:

package docs:adder@0.1.0;

interface add {
    variant computation-error {
        overflow
    }
    add: func(x: u32, y: u32) -> result<u32, computation-error>;
}

world adder {
    import add;
}

将生成以下类型和函数:

// Generated by `wit-bindgen` 0.45.0. DO NOT EDIT!

///|
pub(all) suberror ComputationError {
  Overflow
} derive(Show, Eq)

///|
pub fn add(x : UInt, y : UInt) -> Result[UInt, ComputationError] {
  ...
}

你可以这样使用它:

// Generated by `wit-bindgen` 0.45.0.

///|
fn init {
  let _ = @add.add(1, 2).unwrap_or_error() catch { Overflow => ... }

}

–ignore-stub#

当你想要由于更新接口而重新生成项目时,但又不想修改 stub 文件时,就会发生这种情况。你可以使用 --ignore-stub 选项来避免这种修改。

–project-name#

默认情况下,项目名称是根据 MoonBit 文件中定义的名称生成的。你可以使用此选项来指定项目的名称。如果你正在将项目作为更大项目的一部分生成,它也可以使用。

–gen-dir#

默认情况下,导出部分生成在 gen 下。你可以使用此选项指定另一个目录。

10. References and Further Reading#