Skip to content

Commit d6f3ffc

Browse files
committed
Records and otber WIT UDTs in Rust
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent a087ee7 commit d6f3ffc

File tree

1 file changed

+79
-25
lines changed
  • component-model/src/language-support

1 file changed

+79
-25
lines changed

component-model/src/language-support/rust.md

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,60 @@ $ wasmtime run ./my-composed-command.wasm
276276
1 + 1 = 579 # might need to go back and do some work on the calculator implementation
277277
```
278278

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+
impl Guest for Implementation {
316+
fn eval(expr: Expression) -> u32 {
317+
// Record fields become public fields on a struct
318+
let (l, r) = (expr.left, expr.right);
319+
match expr.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+
279330
## Using resources
280331

281-
[Resources](../design/wit.md#resources) are handles to entities that live outside the component, for example in a host, or in a different component. You can import or export resources via `cargo component`.
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.
282333

283334
### Example
284335

@@ -288,23 +339,23 @@ In this section, our example resource will be a [Reverse Polish Notation (RPN)](
288339
package docs:rpn@0.1.0;
289340
290341
interface types {
291-
enum operation {
292-
add,
293-
sub,
294-
mul,
295-
div
296-
}
297-
298-
resource engine {
299-
constructor();
300-
push-operand: func(operand: u32);
301-
push-operation: func(operation: operation);
302-
execute: func() -> u32;
303-
}
342+
enum operation {
343+
add,
344+
sub,
345+
mul,
346+
div
347+
}
348+
349+
resource engine {
350+
constructor();
351+
push-operand: func(operand: u32);
352+
push-operation: func(operation: operation);
353+
execute: func() -> u32;
354+
}
304355
}
305356
306357
world calculator {
307-
export types;
358+
export types;
308359
}
309360
```
310361

@@ -326,9 +377,11 @@ struct CalcEngine {
326377

327378
> 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>>`.
328379
329-
3. The generated bindings 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:
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:
330381

331382
```rust
383+
use bindings::exports::docs::rpn::types::{GuestEngine, Operation};
384+
332385
impl GuestEngine for CalcEngine {
333386
fn new() -> Self {
334387
CalcEngine {
@@ -354,7 +407,7 @@ impl GuestEngine for CalcEngine {
354407
}
355408

356409
fn execute(&self) -> u32 {
357-
return self.stack.borrow_mut().pop().unwrap(); // TODO: error handling!
410+
self.stack.borrow_mut().pop().unwrap() // TODO: error handling!
358411
}
359412
}
360413
```
@@ -367,7 +420,7 @@ impl Guest for Implementation {
367420
type Engine = CalcEngine;
368421
}
369422

370-
export!(Implementation);
423+
bindings::export!(Implementation with_types_in bindings);
371424
```
372425

373426
This completes the implementation of the calculator `engine` resource. Run `cargo component build` to create a component `.wasm` file.
@@ -378,7 +431,7 @@ To use the calculator engine in another component, that component must import th
378431

379432
1. Create a command component as shown in previous sections.
380433

381-
1. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types:
434+
2. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types:
382435

383436
```wit
384437
package docs:rpn-cmd;
@@ -406,12 +459,13 @@ path = "wit"
406459
```rust
407460
#[allow(warnings)]
408461
mod bindings;
462+
use bindings::docs::rpn::types::{Engine, Operation};
409463

410464
fn main() {
411-
let calc = bindings::docs::rpn::types::Engine::new();
465+
let calc = Engine::new();
412466
calc.push_operand(1);
413467
calc.push_operand(2);
414-
calc.push_operation(bindings::docs::rpn::types::Operation::Add);
468+
calc.push_operation(Operation::Add);
415469
let sum = calc.execute();
416470
println!("{sum}");
417471
}
@@ -454,8 +508,8 @@ struct CalcEngine { /* ... */ }
454508

455509
```rust
456510
impl docs::rpn::types::HostEngine for MyHost {
457-
fn new(&mut self) -> wasmtime::component::Resource<docs::calculator::types::Engine> { /* ... */ }
458-
fn push_operand(&mut self, self_: wasmtime::component::Resource<docs::calculator::types::Engine>) { /* ... */ }
511+
fn new(&mut self) -> wasmtime::component::Resource<docs::rpn::types::Engine> { /* ... */ }
512+
fn push_operand(&mut self, self_: wasmtime::component::Resource<docs::rpn::types::Engine>) { /* ... */ }
459513
// etc.
460514
}
461515
```
@@ -474,10 +528,10 @@ struct MyHost {
474528

475529
```rust
476530
impl docs::rpn::types::HostEngine for MyHost {
477-
fn new(&mut self) -> wasmtime::component::Resource<docs::calculator::types::Engine> {
531+
fn new(&mut self) -> wasmtime::component::Resource<docs::rpn::types::Engine> {
478532
self.calcs.push(CalcEngine::new()).unwrap() // TODO: error handling
479533
}
480-
fn push_operand(&mut self, self_: wasmtime::component::Resource<docs::calculator::types::Engine>) {
534+
fn push_operand(&mut self, self_: wasmtime::component::Resource<docs::rpn::types::Engine>) {
481535
let calc_engine = self.calcs.get(&self_).unwrap();
482536
// calc_engine is a CalcEngine - call its functions
483537
}

0 commit comments

Comments
 (0)