Skip to content

Is it possible to mix imported and exported resources? #544

@Sintrastes

Description

@Sintrastes

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions