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 工具链#
安装
wit-bindgenCLI 工具,它可以从 WIT 文件生成 MoonBit 绑定:$ cargo install wit-bindgen-cli
为了使用 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 下。你可以使用此选项指定另一个目录。