WebAssembly / WebAssembly/component-model

Is it possible to mix imported and exported resources?

Open
#544 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
WebAssembly
Stars
1.4k
Forks
130
Avg merge
2d 1h
Merged PRs (30d)
15

Description

Hello!

First off, I am not sure if this is the proper place for this issue, as I don't know if this issue is a problem with the component model itself, or if the issue is just with cargo-component. If this is not an issue with the component model / WIT itself, I am happy to move this issue to the appropriate repo.

Since I understand that stream<T> is still a work in progress, I decided to try to roll my own WIT worlds / interfaces for reactive flows. Since WIT does not currently have first-class functions / callback types, I tried to emulate this via resource types. I did something like:

interface callbacks {
    resource s32-update-handler {
        on-update: func(value: s32);
    }
}

interface reactive-values {
    resource s32-reactive-value {
        current-value: func() -> s32;
        register-update-handler: func(handler: s32-update-handler);
        unregister-update-handler: func(handler: s32-update-handler);
    }

    new-s32-reactive-value: func(initial: s32) -> tuple<s32-update-handler, s32-reactive-value>;
}

world my-world {
    export callbacks;
    import reactive-values;
}

The callbacks are exported so that the client component can implement the callbacks natively, and the reactive-values are imported, because this is something that should be implemented by the host / another wasm component (to keep the implementation of the reactive values decoupled from the client component).

However, when I try to use this WIT to implement a component with cargo-component, with the following snippet:

let (set_x, x) = new_s32_reactive_value(0);
let (set_y, y) = new_s32_reactive_value(0);

let (set_x_y_sum, x_y_sum) = new_string_reactive_value("0");

x.register_update_handler(S32UpdateHandler::new(IntCallback(Box::new(|x| {
    let y = y.current_value();
    let sum = (x + y).to_string();
    set_x_y_sum.on_update(sum);
}))));

y.register_update_handler(S32UpdateHandler::new(IntCallback(Box::new(|y| {
    let x = x.current_value();
    let sum = (x + y).to_string();
    set_x_y_sum.on_update(&sum);
}))));

I get the error:

mismatched types
`S32UpdateHandler` and `S32UpdateHandler` have similar names, but are actually distinct typesrustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20[5]?5#file:///Users/nathan/Code/wasm-rust-gui-example/src/lib.rs)
lib.rs(32, 11): arguments to this method are incorrect
bindings.rs(5222, 17): `S32UpdateHandler` is defined in module `crate::bindings::exports::component::wasm_rust_gui_example::update_handlers` of the current crate
bindings.rs(502, 13): `S32UpdateHandler` is defined in module `crate::bindings::component::wasm_rust_gui_example::update_handlers` of the current crate
bindings.rs(1254, 24): method defined here

So it seems to me like cargo-component is generating two separate types for S32UpdateHandler for imported versions of the type, and exported versions of the type, which makes sense to me -- because obviously the implementation is going to be different depending on whether the resource is coming from your component, or from an external component.

However, what I really want is (to make up some syntax for an "imported" and "exported" modality for types:

imported resource s32-reactive-value {
      current-value: func() -> s32;
      register-update-handler: func(handler: exported s32-update-handler);
      unregister-update-handler: func(handler: exported s32-update-handler);
}

In other words, to be able to use callbacks (update handlers) that have been exported from the client within an imported resource type. But since there isn't a way of expressing the "import / export" modality (for lack of a better term) of the types in such a granular way, I am not sure how to accomplish this.

Is it possible to do what I am trying to do here in WIT today and maybe I am just doing things wrong? Do the semantics of imported / exported resources need to be refined / clarified in order to support this kind of use-case?

Note: I can provide a trimmed down minimally reproducible example if needed.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the WIT interfaces and world in the issue, then inspect the generated bindings.rs types and reproduce the cargo-component compiler diagnostic. Determine whether imported resources can accept exported resource types, and document the supported semantics or the design needed to make this use case possible.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust, wasm
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.