Skip to content

Commit 0ac2dca

Browse files
refactor(lang/rust): WIT resources guide for rust
1 parent 69ac5e7 commit 0ac2dca

File tree

1 file changed

+45
-25
lines changed
  • component-model/src/language-support/using-wit-resources

1 file changed

+45
-25
lines changed

component-model/src/language-support/using-wit-resources/rust.md

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
[Resources](../design/wit.md#resources) are handles to entities that live outside the component (i.e. in a host, or other component).
44

5-
## An example stack-based calculator
5+
## An example stack-based Reverse Polish Notation (RPN) calculator
66

7-
In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.) A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack maintained within the calculator, then evaluates the stack to produce a value. The resource in WIT looks like this:
7+
In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.)
8+
9+
A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack
10+
maintained within the calculator, then evaluates the stack to produce a value.
11+
12+
In WIT, the resource looks like the following:
813

914
```wit
1015
package docs:rpn@0.1.0;
@@ -32,7 +37,7 @@ world calculator {
3237

3338
## Implementing and exporting a resource in a component
3439

35-
To implement the calculator using `cargo component`:
40+
To implement the calculator in Rust:
3641

3742
1. Create a library component as shown in previous sections, with the WIT given above.
3843

@@ -48,9 +53,15 @@ To implement the calculator using `cargo component`:
4853

4954
> Why is the stack wrapped in a `RefCell`? As we will see, the generated Rust trait for the calculator engine has _immutable_ references to `self`. But our implementation of that trait will need to mutate the stack. So we need a type that allows for interior mutability, such as `RefCell<T>` or `Arc<RwLock<T>>`.
5055

51-
3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. (You may need to run `cargo component build` to regenerate the bindings after updating the WIT.) For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2:
56+
3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2:
5257

5358
```rust
59+
mod bindings {
60+
use super::Component;
61+
wit_bindgen::generate!();
62+
export!(Component);
63+
}
64+
5465
use bindings::exports::docs::rpn::types::{GuestEngine, Operation};
5566

5667
impl GuestEngine for CalcEngine {
@@ -86,23 +97,26 @@ To implement the calculator using `cargo component`:
8697
4. We now have a working calculator type which implements the `engine` contract, but we must still connect that type to the `engine` resource type. This is done by implementing the generated `Guest` trait. For this WIT, the `Guest` trait contains nothing except an associated type. You can use an empty `struct` to implement the `Guest` trait on. Set the associated type for the resource - in our case, `Engine` - to the type which implements the resource trait - in our case, the `CalcEngine` `struct` which implements `GuestEngine`. Then use the `export!` macro to export the mapping:
8798

8899
```rust
89-
struct Implementation;
90-
impl Guest for Implementation {
100+
// ... bindings & CalcEngine impl code ...
101+
102+
struct Component;
103+
104+
impl bindings::Guest for Component {
91105
type Engine = CalcEngine;
92106
}
93107

94-
bindings::export!(Implementation with_types_in bindings);
108+
bindings::export!(Component);
95109
```
96110

97-
This completes the implementation of the calculator `engine` resource. Run `cargo component build` to create a component `.wasm` file.
111+
This completes the implementation of the calculator `engine` resource. Run `cargo build --target=wasm32-wasip2` to create a component `.wasm` file.
98112

99113
## Importing and consuming a resource in a component
100114

101115
To use the calculator engine in another component, that component must import the resource.
102116

103-
1. Create a command component as shown in previous sections.
117+
1. [Create a runnable component](../../creating-runnable-components/rust.md) as shown in previous sections.
104118

105-
2. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types:
119+
2. Add a `wit/component.wit` to your project, and write a WIT world that imports the RPN calculator types:
106120

107121
```wit
108122
package docs:rpn-cmd;
@@ -112,24 +126,24 @@ To use the calculator engine in another component, that component must import th
112126
}
113127
```
114128

115-
3. Edit `Cargo.toml` to tell `cargo component` about the new WIT file and the external RPN package file:
129+
3. Create a `wkg.toml` file to enable retrieving the relevant WIT files for `docs:rpn` (which contains the `engine` resource):
116130

117131
```toml
118-
[package.metadata.component]
119-
package = "docs:rpn-cmd"
120-
121-
[package.metadata.component.target]
122-
path = "wit"
123-
124-
[package.metadata.component.target.dependencies]
125-
"docs:rpn" = { path = "../wit" } # or wherever your resource WIT is
132+
[overrides]
133+
"docs:rpn" = { path = "../path/to/docs-rpn/wit" }
126134
```
127135

136+
After doing this, you can run `wkg wit fetch` to ensure all WIT is available locally.
137+
128138
4. The resource now appears in the generated bindings as a `struct`, with appropriate associated functions. Use these to construct a test app:
129139

130140
```rust
131-
#[allow(warnings)]
132-
mod bindings;
141+
mod bindings {
142+
use super::Component;
143+
wit_bindgen::generate!();
144+
export!(Component);
145+
}
146+
133147
use bindings::docs::rpn::types::{Engine, Operation};
134148

135149
fn main() {
@@ -142,11 +156,19 @@ To use the calculator engine in another component, that component must import th
142156
}
143157
```
144158

145-
You can now build the command component and [compose it with the `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). You can then run the composed command with `wasmtime run`.
159+
Building the component as is creates a WebAssembly component with an *unsatisfied import* -- namely the `docs:rpn/types` import.
160+
161+
After building the component, it must be [composed with a `.wasm` component that implements the resource.](../composing-and-distributing/composing.md). After composition creates a component with no unsatisfied imports, the composed command component can be run with `wasmtime run`.
162+
163+
Alternatively, a host that can provide the `docs:rpn/types` import (and related resource) can also be used to run the component
164+
in it's "incomplete" state (as the host will "complete" the componnt by providing the expected import).
146165

147166
## Implementing and exporting a resource implementation in a host
148167

149-
If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume. Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific to the Wasmtime runtime; other runtimes may express things differently.
168+
If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume.
169+
170+
Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific
171+
to the Wasmtime runtime; other runtimes may express things differently.
150172

151173
1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for:
152174

@@ -210,8 +232,6 @@ If you are hosting a Wasm runtime, you can export a resource from your host for
210232
}
211233
```
212234

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

217237
[!NOTE]: #

0 commit comments

Comments
 (0)