Skip to content

docs: Clarify setup for "Rust Components" tutorial #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion component-model/examples/example-host/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ $ cargo run --release -- 1 2 add.wasm
```

> [!NOTE]
> `add.wasm` is available in thsi folder, but can be replaced with your own built WebAssembly component
> `add.wasm` is available in this folder, but can be replaced with your own built WebAssembly component
> at any time (written in any language that supports WebAssembly Components), given that it satisfies
> the `adder` world described above.

Expand Down
27 changes: 16 additions & 11 deletions component-model/examples/tutorial/adder/src/bindings.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT!
// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT!
// Options used:
// * runtime_path: "wit_bindgen_rt"
#[rustfmt::skip]
#[allow(dead_code, clippy::all)]
pub mod exports {
pub mod docs {
pub mod adder {
#[allow(dead_code, clippy::all)]
#[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)]
pub mod add {
#[used]
#[doc(hidden)]
Expand All @@ -25,9 +25,10 @@ pub mod exports {
#[doc(hidden)]
macro_rules! __export_docs_adder_add_0_1_0_cabi {
($ty:ident with_types_in $($path_to_types:tt)*) => {
const _ : () = { #[export_name = "docs:adder/[email protected]#add"]
unsafe extern "C" fn export_add(arg0 : i32, arg1 : i32,) -> i32 {
$($path_to_types)*:: _export_add_cabi::<$ty > (arg0, arg1) } };
const _ : () = { #[unsafe (export_name =
"docs:adder/[email protected]#add")] unsafe extern "C" fn export_add(arg0
: i32, arg1 : i32,) -> i32 { unsafe { $($path_to_types)*::
_export_add_cabi::<$ty > (arg0, arg1) } } };
};
}
#[doc(hidden)]
Expand All @@ -38,6 +39,7 @@ pub mod exports {
}
#[rustfmt::skip]
mod _rt {
#![allow(dead_code, clippy::all)]
#[cfg(target_arch = "wasm32")]
pub fn run_ctors_once() {
wit_bindgen_rt::run_ctors_once();
Expand Down Expand Up @@ -102,8 +104,8 @@ mod _rt {
}
}
}
/// Generates `#[no_mangle]` functions to export the specified type as the
/// root implementation of all generated traits.
/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as
/// the root implementation of all generated traits.
///
/// For more information see the documentation of `wit_bindgen::generate!`.
///
Expand Down Expand Up @@ -133,14 +135,17 @@ macro_rules! __export_adder_impl {
#[doc(inline)]
pub(crate) use __export_adder_impl as export;
#[cfg(target_arch = "wasm32")]
#[link_section = "component-type:wit-bindgen:0.36.0:docs:[email protected]:adder:encoded world"]
#[unsafe(
link_section = "component-type:wit-bindgen:0.41.0:docs:[email protected]:adder:encoded world"
)]
#[doc(hidden)]
#[allow(clippy::octal_escapes)]
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 203] = *b"\
\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07P\x01A\x02\x01A\x02\x01\
B\x02\x01@\x02\x01ay\x01by\0y\x04\0\x03add\x01\0\x04\0\x14docs:adder/[email protected]\x05\
B\x02\x01@\x02\x01xy\x01yy\0y\x04\0\x03add\x01\0\x04\0\x14docs:adder/[email protected]\x05\
\0\x04\0\x16docs:adder/[email protected]\x04\0\x0b\x0b\x01\0\x05adder\x03\0\0\0G\x09pr\
oducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.220.0\x10wit-bindgen-rust\x06\
0.36.0";
oducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.227.1\x10wit-bindgen-rust\x06\
0.41.0";
#[inline(never)]
#[doc(hidden)]
pub fn __link_custom_section_describing_imports() {
Expand Down
7 changes: 7 additions & 0 deletions component-model/examples/tutorial/adder/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#[allow(warnings)]
mod bindings;

// The comments that follow the `use` declaration below
// correlate the rust module path segments with their
// `world.wit` counterparts:
use bindings::exports::docs::adder::add::Guest;
// <- items bundled with `export` keyword
// <- package namespace
// <- package
// <- interface name

struct Component;

Expand Down
4 changes: 2 additions & 2 deletions component-model/src/language-support/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ With `jco transpile` any WebAssembly binary (compiled from any language) can be

Reactor components are WebAssembly components that are long running and meant to be called repeatedly over time. They're analogous to libraries of functionality rather than an executable (a "command" component).

Components expose their interfaces via [WebAssembly Interface Types][docs-wit], hand-in-hand with the [Component Model][docs-component-model] which enables components to use higher level types interchangably.
Components expose their interfaces via [WebAssembly Interface Types][docs-wit], hand-in-hand with the [Component Model][docs-component-model] which enables components to use higher level types interchangeably.


[docs-wit]: ../design/wit.md
Expand Down Expand Up @@ -314,7 +314,7 @@ You should see output like the following:
OK Successfully written string-reverse.wasm.
```

Now that we have a WebAssembly binary, we can *also* use `jco` to run it in a native JavaScript context by *transpiling* the WebAsssembly binary (which could have come from anywhere!) to a JavaScript module.
Now that we have a WebAssembly binary, we can *also* use `jco` to run it in a native JavaScript context by *transpiling* the WebAssembly binary (which could have come from anywhere!) to a JavaScript module.

```console
npx jco transpile string-reverse.wasm -o dist/transpiled
Expand Down
137 changes: 72 additions & 65 deletions component-model/src/language-support/rust.md
Original file line number Diff line number Diff line change
@@ -1,101 +1,99 @@
# Components in Rust

Rust has first-class support for the component model via [the `cargo component` tool](https://github.com/bytecodealliance/cargo-component).
Rust has first-class support for the component model via the [`cargo-component` tool][cargo-component].
We will be using the `cargo component` subcommand to create WebAssembly components using Rust as
the component's implementation language.

`cargo component` is is a `cargo` subcommand for creating WebAssembly components
using Rust as the component's implementation language.
> [!NOTE]
> You can find more details about `cargo-component` on [crates.io](https://crates.io/crates/cargo-component).

## 1. Installing `cargo component`

To install `cargo component`, run:
## 1. Setup

Install [`cargo-component`][cargo-component-install]:
```sh
cargo install cargo-component
cargo install --locked cargo-component
```

> You can find more details about `cargo component` in its [crates.io page](https://crates.io/crates/cargo-component).

## 2. Scaffold a Component with `cargo component`

Create a Rust library that implements the `add` function in the [`adder` world][adder-wit].

First scaffold a project:

Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools#installation):
```sh
$ cargo component new adder --lib && cd adder
cargo install --locked wasm-tools
```
Install [`wasmtime`](https://github.com/bytecodealliance/wasmtime#installation):
```sh
curl https://wasmtime.dev/install.sh -sSf | bash
```

Note that `cargo component` generates the necessary bindings as a module called `bindings`.

## 3. Add the WIT world the Component will implement
## 2. Scaffolding a Component

Next, update `wit/world.wit` to match the [`adder` world][adder-wit]:
We will create a component in Rust that implements the `add` function exported
by the [`adder` world][docs-adder] world in the `docs:adder`
[package](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#package-names).

First, we will create a new WebAssembly component package called `add`:
```sh
cargo component new add --lib && cd add
```
package docs:[email protected];

interface add {
add: func(x: u32, y: u32) -> u32;
}
## 3. Adding the WIT world

world adder {
export add;
}
We now need to change our generated `wit/world.wit` to match `docs:adder`:
```wit
{{#include ../../examples/tutorial/wit/adder/world.wit}}
```

The `component` section of `Cargo.toml` should look like the following:
The `package.metadata.component` section of our `Cargo.toml` should be changed
to the following:

```toml
[package.metadata.component]
package = "docs:adder"
```

## 4. Generate bindings for our component
## 4. Generating bindings

After performing these changes, we can re-generate bindings with `cargo component bindings`:
Now that we've updated our `world.wit` and `Cargo.toml`, we can re-generate
bindings with the command below:

```console
```sh
cargo component bindings
```

`cargo component bindings` will generate bindings for the world specified in a package's `Cargo.toml`. In particular,
`cargo component` will create a `Guest` trait that a component should implement.
`cargo-component` will generate bindings for our
world and create a `Guest` trait that a component should
implement.

## 5. Implement the generated `Guest` trait
## 5. Implementing the `Guest` trait

Implement the `Guest` trait in `src/lib.rs`, using the scaffolded code. Your code should look something like the following:

```rs
#[allow(warnings)]
mod bindings;

use bindings::exports::docs::adder::add::Guest;

struct Component;

impl Guest for Component {
fn add(x: u32, y: u32) -> u32 {
return x + y;
}
}

bindings::export!(Component with_types_in bindings);
{{#include ../../examples/tutorial/adder/src/lib.rs}}
```

## 6. Build the component
## 6. Building a Component

Now, use `cargo component` to build the component, being sure to optimize with a release build.
Now, let's build our component, being sure to optimize with a release build:

```console
```sh
cargo component build --release
```

> **WARNING:** Building with `--release` removes all debug-related information from the resulting .wasm file. When prototyping or testing locally, you might want to avoid `--release` to obtain useful backtraces in case of errors (for example, with `wasmtime::WasmBacktraceDetails::Enable`). Note: the resulting .wasm file will be considerably larger (likely 4MB+).
> [!WARNING]
> Building with `--release` removes all debug-related information from the resulting `.wasm` file.
>
> When prototyping or testing locally, you might want to avoid `--release` to
> obtain useful backtraces in case of errors (for example, with
> [`wasmtime::WasmBacktraceDetails::Enable`](https://docs.rs/wasmtime/latest/wasmtime/enum.WasmBacktraceDetails.html#variant.Enable)).
> Note: the resulting `.wasm` file will be considerably larger (likely 4MB+).

You can use `wasm-tools component wit` to output the WIT package of the component:
You can use `wasm-tools` to output the WIT package of the component:

```sh
wasm-tools component wit target/wasm32-wasip1/release/add.wasm
```
$ wasm-tools component wit target/wasm32-wasip1/release/adder.wasm

The command above should produce the output below:

```wit
package root:component;

world root {
Expand All @@ -108,27 +106,31 @@ package docs:[email protected] {
}
```

### Running a Component from Rust Applications

### Running a Component

To verify that our component works, lets run it from a Rust application that knows how to run a
component targeting the [`adder` world][adder-wit].
component targeting the [`adder` world](#adding-the-wit-world).

The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime) crates to generate
Rust bindings, bring in WASI worlds, and execute the component.

```sh
```console
$ cd examples/example-host
$ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/adder.wasm
1 + 2 = 3
```

## Importing an interface with `cargo component`
## Importing an interface

The world file (`wit/world.wit`) generated for you by `cargo component new --lib` doesn't specify any imports.
The world file (`wit/world.wit`) we generated doesn't specify any imports.
If your component consumes other components, you can edit the `world.wit` file to import their interfaces.

> `cargo component build`, by default, uses the Rust `wasm32-wasi` target, and therefore automatically imports any required WASI interfaces - no action is needed from you to import these. This section is about importing custom WIT interfaces from library components.
> [!NOTE]
> This section is about importing custom WIT interfaces from library components.
> By default, `cargo-component` imports any required [WASI interfaces](https://wasi.dev/interfaces)
> for us without needing to explicitly declare them.

If your component consumes other components, you can edit the `world.wit` file to import their interfaces.

For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface:

Expand Down Expand Up @@ -157,7 +159,10 @@ Because the `docs:adder` package is in a different project, we must first tell `
"docs:adder" = { path = "../adder/wit" } # directory containing the WIT package
```

Note that the path is to the adder project's WIT _directory_, not to the `world.wit` file. A WIT package may be spread across multiple files in the same directory; `cargo component` will look at all the files.
> [!NOTE]
> The path for `docs:adder` is relative to the `wit` _directory_, not to the `world.wit` file.
>
> A WIT package may be spread across multiple files in the same directory; `cargo component` will search them all.

### Calling the import from Rust

Expand Down Expand Up @@ -283,7 +288,7 @@ As mentioned above, `cargo component build` doesn't generate a WIT file for a co

6. Run the composed component:

```sh
```console
$ wasmtime run ./my-composed-command.wasm
1 + 1 = 579 # might need to go back and do some work on the calculator implementation
```
Expand Down Expand Up @@ -551,4 +556,6 @@ If you are hosting a Wasm runtime, you can export a resource from your host for
}
```

[adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit
[cargo-component]: https://github.com/bytecodealliance/cargo-component
[cargo-component-install]: https://github.com/bytecodealliance/cargo-component#install
[docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit