This repository contains a sample demonstrating the use of the WebAssembly (Wasm) Component Model.
This example implements a simple calculator using the Wasm Component Model. The calculator can perform basic operations such as addition, subtraction, and multiplication.
Right after cloning, the component structure is as follows.
The multiplier component will be added later in a tutorial-like format in the Adding New Components section.
flowchart LR
app --> calculator
calculator --> adder
calculator --> subtractor
| Language/Tool | Description |
|---|---|
| Rust | A systems programming language focusing on speed, memory safety, and parallelism. Used here for implementing WebAssembly components. |
| cargo component | A command-line tool used for building WebAssembly components. |
| WebAssembly Compositions (WAC) | A tool used for composing WebAssembly components. |
| Wasmtime | A fast and secure runtime for executing WebAssembly modules, including support for the Wasm Component Model. |
Run the following commands to build each component:
# Build the adder component
cd adder && cargo component build --release && cd ../
# Build the subtractor component
cd subtractor && cargo component build --release && cd ../
# Build the calculator component
cd calculator && cargo component build --release && cd ../
# Build the app component
cargo component build --releaseNext, compose the components together using the WAC tool:
# Plug calculator with adder and subtractor components
wac plug calculator/target/wasm32-wasip1/release/calculator.wasm \
--plug adder/target/wasm32-wasip1/release/adder.wasm \
--plug subtractor/target/wasm32-wasip1/release/subtractor.wasm \
-o composed.wasm
# Plug app with the composed module
wac plug target/wasm32-wasip1/release/app.wasm \
--plug composed.wasm \
-o main.wasmRun the WebAssembly module using Wasmtime:
wasmtime run main.wasm 2 1 add
# => 2 + 1 = 3
wasmtime run main.wasm 2 1 sub
# => 2 - 1 = 1To add a new component, follow these steps.
In this example, a multiplier component will be added for multiplication.
flowchart LR
app --> calculator
calculator --> adder
calculator --> subtractor
calculator --> MUL["multiplier (new)"]
Use the following command to create a new library component:
cargo component new --lib multiplierNote: For a library component, specify --lib. For a command component, specify --command or leave it unspecified when initializing the project.
Create a WIT file to define the multiplier component:
wit/multiplier/world.wit
package component:multiplier@0.1.0;
interface multiply {
multiply: func(x: u32, y: u32) -> u32;
}
world multiplier {
export multiply;
}For details on the WIT specifications, please refer to the official documentations (WIT - The WebAssembly Component Model, The wit format - GitHub).
Update the Cargo.toml with the component's information (such as the location of the WIT file and dependencies):
multiplier/Cargo.toml
# ...
[package.metadata.component]
package = "component:multiplier"
[package.metadata.component.dependencies]
[package.metadata.component.target]
path = "../wit/multiplier"
world = "multiplier"
[package.metadata.component.target.dependencies]Run the following command to generate the required bindings:
cd multiplier && cargo component build && cd ../Ensure that multiplier/src/bindings.rs has been generated.
Troubleshooting
The following error may occur, but you can safely ignore it.error[E0432]: unresolved import `bindings::Guest`
--> src/lib.rs:4:5
|
4 | use bindings::Guest;
| ^^^^^^^^^^^^^^^ no `Guest` in `bindings`
|
help: consider importing this trait instead
|
4 | use crate::bindings::exports::component::multiplier::multiply::Guest;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For more information about this error, try `rustc --explain E0432`.
error: could not compile `multiplier` (lib) due to 1 previous errorThe subsequent steps assume that your current directory is the project root, so please move there if necessary.
Finally, implement the Guest trait:
multiplier/src/lib.rs
#[allow(warnings)]
mod bindings;
use bindings::exports::component::multiplier::multiply::Guest;
struct Component;
impl Guest for Component {
fn multiply(x: u32, y: u32) -> u32 {
x * y
}
}
bindings::export!(Component with_types_in bindings);Update the calculator component's WIT file to import the multiplier:
wit/calculator/world.wit
package component:calculator@0.1.0;
interface calculate {
enum op {
add,
sub,
mul, // New operation
}
calculate: func(op: op, x: u32, y: u32) -> u32;
}
world calculator {
import component:adder/add@0.1.0;
import component:subtractor/subtract@0.1.0;
import component:multiplier/multiply@0.1.0; // New import
export calculate;
}Update the dependencies:
calculator/Cargo.toml
# ...
[package.metadata.component.target.dependencies]
"component:adder" = { path = "../wit/adder" }
"component:subtractor" = { path = "../wit/subtractor" }
"component:multiplier" = { path = "../wit/multiplier" } # New dependencyRun the following command to regenerate the bindings:
cd calculator && cargo component build && cd ../Troubleshooting
The following error may occur, but you can safely ignore it.error[E0004]: non-exhaustive patterns: `Op::Mul` not covered
--> src/lib.rs:11:15
|
11 | match op {
| ^^ pattern `Op::Mul` not covered
|
note: `Op` defined here
--> src/bindings.rs:99:26
|
99 | pub enum Op {
| ^^
...
102 | Mul,
| --- not covered
= note: the matched value is of type `Op`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
13 ~ Op::Sub => subtract(x, y),
14 ~ Op::Mul => todo!(),
|
For more information about this error, try `rustc --explain E0004`.
error: could not compile `calculator` (lib) due to 1 previous errorThe subsequent steps assume that your current directory is the project root, so please move there if necessary.
Modify the Guest trait:
calculator/src/lib.rs
// ...
impl Guest for Component {
fn calculate(op: Op, x: u32, y: u32) -> u32 {
match op {
Op::Add => add(x, y),
Op::Sub => subtract(x, y),
Op::Mul => multiply(x, y), // New operation
}
}
}
// ...Update the dependencies:
Cargo.toml
# ...
[package.metadata.component.target.dependencies]
"component:calculator" = { path = "wit/calculator" }
"component:adder" = { path = "wit/adder" }
"component:subtractor" = { path = "wit/subtractor" }
"component:multiplier" = { path = "wit/multiplier" } # New dependencyRun the following command to regenerate the bindings:
cargo component buildTroubleshooting
The following error may occur, but you can safely ignore it.error[E0004]: non-exhaustive patterns: `&Op::Mul` not covered
--> src/main.rs:18:15
|
18 | match self {
| ^^^^ pattern `&Op::Mul` not covered
|
note: `Op` defined here
--> src/bindings.rs:13:22
|
13 | pub enum Op {
| ^^
...
16 | Mul,
| --- not covered
= note: the matched value is of type `&Op`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
20 ~ Op::Sub => write!(f, "-"),
21 ~ &Op::Mul => todo!(),
|
For more information about this error, try `rustc --explain E0004`.
error: could not compile `app` (bin "app") due to 1 previous errorUpdate main.rs to parse the new operation:
src/main.rs
// ...
fn parse_operator(op: &str) -> anyhow::Result<Op> {
match op {
"add" => Ok(Op::Add),
"sub" => Ok(Op::Sub),
"mul" => Ok(Op::Mul), // New operation
_ => anyhow::bail!("Unknown operation: {}", op),
}
}
impl fmt::Display for Op {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Op::Add => write!(f, "+"),
Op::Sub => write!(f, "-"),
Op::Mul => write!(f, "*"), // New operator display
}
}
}
// ...# Build the multiplier component
cd multiplier && cargo component build --release && cd ../
# Build the calculator component
cd calculator && cargo component build --release && cd ../
# Build the app component
cargo component build --release# Plug calculator with adder, subtractor, and multiplier components
wac plug calculator/target/wasm32-wasip1/release/calculator.wasm \
--plug adder/target/wasm32-wasip1/release/adder.wasm \
--plug subtractor/target/wasm32-wasip1/release/subtractor.wasm \
--plug multiplier/target/wasm32-wasip1/release/multiplier.wasm \
-o composed.wasm
# Plug app with the composed module
wac plug target/wasm32-wasip1/release/app.wasm \
--plug composed.wasm \
-o main.wasmwasmtime run main.wasm 2 1 add
# => 2 + 1 = 3
wasmtime run main.wasm 2 1 sub
# => 2 - 1 = 1
wasmtime run main.wasm 2 1 mul
# => 2 * 1 = 2