You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: component-model/src/language-support/rust.md
+263Lines changed: 263 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -275,3 +275,266 @@ fn main() {
275
275
$ wasmtime run ./my-composed-command.wasm
276
276
1 + 1 = 579 # might need to go back and do some work on the calculator implementation
277
277
```
278
+
279
+
## Using user-defined types
280
+
281
+
[User-defined types](../design/wit.md#user-defined-types) map to Rust types as follows.
282
+
283
+
| WIT type | Rust binding |
284
+
|------------|--------------|
285
+
|`record`|`struct` with public fields corresponding to the record fields |
286
+
|`variant`|`enum` with cases corresponding to the variant cases |
287
+
|`enum`|`enum` with cases corresponding to the enum cases, with no data attached |
288
+
|`resource`|[See below](#using-resources)|
289
+
|`flags`| Opaque type supporting bit flag operations, with constants for flag values |
290
+
291
+
For example, consider the following WIT:
292
+
293
+
```wit
294
+
interface types {
295
+
enum operation {
296
+
add,
297
+
sub,
298
+
mul,
299
+
div
300
+
}
301
+
302
+
record expression {
303
+
left: u32,
304
+
operation: operation,
305
+
right: u32
306
+
}
307
+
308
+
eval: func(expr: expression) -> u32;
309
+
}
310
+
```
311
+
312
+
When exported from a component, this could be implemented as:
313
+
314
+
```rust
315
+
implGuestforImplementation {
316
+
fneval(expr:Expression) ->u32 {
317
+
// Record fields become public fields on a struct
318
+
let (l, r) = (expr.left, expr.right);
319
+
matchexpr.operation {
320
+
// Enum becomes an enum with only unit cases
321
+
Operation::Add=>l+r,
322
+
Operation::Sub=>l-r,
323
+
Operation::Mul=>l*r,
324
+
Operation::Div=>l/r,
325
+
}
326
+
}
327
+
}
328
+
```
329
+
330
+
## Using resources
331
+
332
+
[Resources](../design/wit.md#resources) are handles to entities that live outside the component, for example in a host, or in a different component.
333
+
334
+
### Example
335
+
336
+
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:
### Implementing and exporting a resource in a component
363
+
364
+
To implement the calculator using `cargo component`:
365
+
366
+
1. Create a library component as shown in previous sections, with the WIT given above.
367
+
368
+
2. Define a Rust `struct` to represent the calculator state:
369
+
370
+
```rust
371
+
usestd::cell::RefCell;
372
+
373
+
structCalcEngine {
374
+
stack:RefCell<Vec<u32>>,
375
+
}
376
+
```
377
+
378
+
> 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>>`.
379
+
380
+
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:
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:
You can now build the command component and [compose it with the `.wasm` component that implements the resource.](../creating-and-consuming/composing.md). You can then run the composed command with `wasmtime run`.
475
+
476
+
### Implementing and exporting a resource implementation in a host
477
+
478
+
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.
479
+
480
+
1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for:
481
+
482
+
```rust
483
+
wasmtime::component::bindgen!({
484
+
path:"../wit"
485
+
});
486
+
```
487
+
488
+
2. Tell `bindgen!` how you will represent the resource in the host via the `with` field. This can be any Rust type. For example, the RPN engine could be represented by a `CalcEngine` struct:
489
+
490
+
```rust
491
+
wasmtime::component::bindgen!({
492
+
path:"../wit",
493
+
with: {
494
+
"docs:rpn/types/engine":CalcEngine,
495
+
}
496
+
});
497
+
```
498
+
499
+
> If you don't specify the host representation for a resource, it defaults to an empty enum. This is rarely useful as resources are usually stateful.
500
+
501
+
3. If the representation type isn't a built-in type, define it:
502
+
503
+
```rust
504
+
structCalcEngine { /* ... */ }
505
+
```
506
+
507
+
4. As a host, you will already be implementing a `Host` trait. You will now need to implement a `HostX` trait (where `X` is the resource name) _on the same type_ as the `Host` trait:
**Important:** You implement this on the 'overall' host type, *not* on the resource representation! Therefore, the `self` reference in these functions is to the 'overall' host type. For instance methods of the resource, the instance is identified by a second parameter (`self_`), of type `wasmtime::component::Resource`.
518
+
519
+
5. Add a `wasmtime::component::ResourceTable` to the host:
520
+
521
+
```rust
522
+
structMyHost {
523
+
calcs:wasmtime::component::ResourceTable,
524
+
}
525
+
```
526
+
527
+
6. In your resource method implementations, use this table to store and access instances of the resource representation:
0 commit comments