Stream: general

Topic: ✔ How do I add resource imports from host to component?


view this post on Zulip Ari Seyhun (Mar 10 2026 at 14:43):

I'm really struggling to setup wasmtime to let me define a resource which can be called by the component, where the resource lives on the host.

My wit file looks something like this:

package umari:projection;

interface types {
     ... snip
}

interface projection-runner {
    use types.{stored-event-data, dcb-query, error};

    resource projection-state {
        constructor() -> result<projection-state, error>;
        query: func() -> dcb-query;
        handler: func(event: stored-event-data) -> result<_, error>;
    }
}

interface statement {
    use types.{sql, value, sqlite-error};

    resource stmt {
        constructor(sql: sql) -> result<stmt, sqlite-error>;
        execute: func(params: list<value>) -> result<s64, sqlite-error>;
        query: func(params: list<value>) -> result<list<list<tuple<string, value>>>, sqlite-error>;
        query-one: func(params: list<value>) -> result<option<list<tuple<string, value>>>, sqlite-error>;
        query-row: func(params: list<value>) -> result<option<list<tuple<string, value>>>, sqlite-error>;
    }
}

world projection {
    use types.{sql, value, sqlite-error};

    import statement;
    import execute: func(sql: sql, params: list<value>) -> result<s64, sqlite-error>;
    import execute-batch: func(sql: sql) -> result<_, sqlite-error>;
    import last-insert-rowid: func() -> result<s64, sqlite-error>;
    import query-one: func(sql: sql, params: list<value>) -> result<option<list<tuple<string, value>>>, sqlite-error>;
    import query-row: func(sql: sql, params: list<value>) -> result<option<list<tuple<string, value>>>, sqlite-error>;

    export projection-runner;
}

Without the resource definition, I was able to call execute just fine with:

let mut linker = Linker::new(&args.engine);
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
wit::Projection::add_to_linker::<_, HasSelf<_>>(&mut linker, |s| s)?;
linker.root().func_wrap(
    "execute",
    |caller: StoreContextMut<'_, ComponentRunStates>,
     (sql, params): (String, Vec<wit::Value>)| {
        ...
        Ok((res,))
    },
)?;

But since adding the statement resource, I'm getting this error:

component imports resource `statement`, but a matching implementation was not found in the linker

Caused by:
    resource implementation is missing
)

My bindgen looks like this:

wasmtime::component::bindgen!({
    path: "../../wit/projection",
    exports: { default: async },
    with: {
        "umari:projection/statement.stmt": Stmt,
    },
});

pub struct Stmt;

impl ProjectionImports for ComponentRunStates { ... }
impl umari::projection::types::Host for ComponentRunStates {}
impl umari::projection::statement::Host for ComponentRunStates {}
impl umari::projection::statement::HostStmt for ComponentRunStates { ... }

What am I doing wrong? I'm confused where I should be telling wasmtime where my host functions for the statement resource live. Do I need to call linker.root().resource(...) to define it? I don't see that in any of the examples.

view this post on Zulip Alex Crichton (Mar 10 2026 at 14:50):

We have an example online of imported resources but that doesn't cover the add_to_linker part (we should fix that...).

You in theory shouldn't have to do anything with the linker at all, the add_to_linker function should do everything for you. What might be happening though is that bindgen! generates a number of add_to_linker functions, for example one for the top-level world items, one for each interface, and one for everything together. You might be calling the world-specific one rather than the top-level one? I forget what exactly we called it, and I thought in your case it would be wit::Projection::add_to_linker....

view this post on Zulip Alex Crichton (Mar 10 2026 at 14:51):

The error you're getting is a bit confusing too, it's talking about a resource statement when in your WIT statement is an interface. Is this perhaps a case where the guest WIT is out of sync with the host WIT?

view this post on Zulip Ari Seyhun (Mar 10 2026 at 14:56):

Both of your points were spot on! There's two add_to_linker functions:

And yes the wasm module was out of sync, I think that was the main issue! Thanks for the quick response, really happy to see how far this component stuff has come since a couple years ago.

view this post on Zulip Notification Bot (Mar 10 2026 at 14:57):

Ari Seyhun has marked this topic as resolved.

view this post on Zulip Ari Seyhun (Mar 10 2026 at 15:11):

Another question I have is the ResourceTable::push method requires T to be Send. In my case, this module and resource is only ever used from a single thread on the host, and the type is not Send (Sqlite types). I'm assuming I need some unsafe stuff here, but I thought I'd ask in case there's any suggestions, or to help understand why ResourceTable requires resources to be Send.

view this post on Zulip Ari Seyhun (Mar 10 2026 at 15:15):

I think perhaps a solution in my case is store the !Send types in the ComponentRunStates, and use a hashmap with u64 ids or slotmap, where the resource stores the key to an entry there.

view this post on Zulip Ari Seyhun (Mar 10 2026 at 15:23):

Hmm that won't work either, because ComponentRunStates needs to be Send too

view this post on Zulip Alex Crichton (Mar 10 2026 at 15:31):

Unfortunately we don't have a great way to make things conditional in Wasmtime. You can build your own ResourceTable, however, as the one in Wasmtime doesn't reach into any internals or anything like that. Resources are already a level of indirection so while you could use a ResourceTable to indirect again into the store at that point you might as well have the Resources point directly to slots in the store

view this post on Zulip ktz_alias (Mar 10 2026 at 15:32):

@Ari Seyhun bindgen macro of wasmtime has require_store_data_send option.
Setting this option to true always provides Send.

https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html#options-reference

view this post on Zulip Ari Seyhun (Mar 10 2026 at 15:45):

Thanks for the responses. I tried require_store_data_send: false but I don't think that quite solved it. For now I'm using an unsafe impl of Send with heavy documentation and some debug assertion checks.

view this post on Zulip Ari Seyhun (Mar 10 2026 at 15:47):

I have another question: wit_bindgen doesn't seem to generate Clone for resources? I tried specifying it in additional_derives: [Clone], but I don't think that affects resources. Is it recommended I just wrap my resource in an Arc to make it clonable instead?

view this post on Zulip Chris Fallin (Mar 10 2026 at 15:51):

Ari Seyhun said:

I have another question: wit_bindgen doesn't seem to generate Clone for resources? I tried specifying it in additional_derives: [Clone], but I don't think that affects resources. Is it recommended I just wrap my resource in an Arc to make it clonable instead?

Yes, resources are purely linear (or affine) values that represent ownership of something on the other side, so there is no clone primitive. In some WIT I've written recently I added an ad-hoc clone method on resources where appropriate, implemented by an Arc or moral equivalent on the host side; you could also wrap it on the guest side with an Arc if the WIT is fixed for some reason

view this post on Zulip Alex Crichton (Mar 10 2026 at 16:01):

Oh wait hang on lots of stuff happening here. Right require_store_data_send I think is something entirely different from what @Ari Seyhun is running into. The require_store_data_send option has to do with generated wit-bindgen bindings, but it doesn't affect the constraints of ResourceTable. @Ari Seyhun needs to store non-Send values in a ResourceTable which isn't possible, and that's where a store-specific table that looks like ResourceTable is probably the way to go.

For Clone Chris is right that from the guest's perspective resources aren't clonable, but I think @Ari Seyhun's question is about Resource<T> on the host. On the host there's no reason Resource<T> isn't clonable other than an attempt to mimic the affine-ness of the guest but there's also no such guarantee. @Ari Seyhun you can build your own clone-lookalike like this but that's a bit unfortunate. I keep meaning to sit down and just add Clone for Resource<T> because I don't think the lack of clone is buying us much (and multiple people have run into this).

view this post on Zulip Chris Fallin (Mar 10 2026 at 16:09):

ah, sorry, misunderstood the context -- yes, I was speaking at the WIT API/type-system level

view this post on Zulip Alex Crichton (Mar 10 2026 at 16:12):

Is it recommended I just wrap my resource in an Arc to make it clonable instead?

Oh I can also answer this directly -- probably "no" -- Arc is relatively heavyweight vs Resource<T> which is just a 32-bit integer. This may or may not be workable depending on the context though

view this post on Zulip Ari Seyhun (Mar 11 2026 at 04:30):

Sorry if I left some ambiguity. When I was referring to clonable resources, I was actually talking about on the guest wasm side, from the types generated by the wit_bindgen::generate! macro. The reason I ask is because all the methods generated for a resource on the guest side take only &self, so I was thinking it would be safe to clone the resource "handle".

view this post on Zulip Ari Seyhun (Mar 11 2026 at 04:40):

Sorry for extending this thread so long, but I do have another question.

In my wit directory, I would like to define multiple packages in individual files: umari:command, umari:projection, etc. However, when I do this, and I point wasmtime's bindgen macro to the wit directory and specify the world to use, it has a compile error telling me multiple packages were found. I guess the macros don't support a wit directory with multiple packages defined?

view this post on Zulip Ari Seyhun (Mar 11 2026 at 04:57):

Is this the way I should share types between my packages?

  wit/
    projection/
      projection.wit
      deps/common.wit           symlink from common/types.wit
    command/
      command.wit
      deps/common.wit           symlink from common/types.wit
    common/
      types.wit                 source of truth

And my bindgen! macro call needs to set the path to wit/projection or wit/command instead of just wit?

view this post on Zulip Chris Fallin (Mar 11 2026 at 14:02):

Looking at the internals of whats generated by wit_bindgen, a resource is just an AtomicU32. In Rust AtomicU32 isn't clonable, which explains the reasoning I guess.

Note that that's not the true reason -- if it were just "oops our internals aren't Clone" we could change that. Lack of Clone on the guest-side resource handle is reflecting the actual semantics of the WIT, which is that a resource is an owned thing that has no built-in notion of cloning or refcounted sharing. Wrapping it in an Arc to do that refcounted sharing is a reasonable solution imho.

I guess the macros don't support a wit directory with multiple packages defined?

Indeed, there's a weird (to me -- would be interested in hearing about its reasoning) design restriction in wit-bindgen that a directory can contain only one package. Also a directory within the tree has to contain a package, so e.g. you can't have myorg/a and myorg/b without a WIT package in myorg/. (I wish we could relax this.)

However you should be able to have a unified hierarchy without symlinks or copies -- if you construct a tree according to the rules above, every bindgen macro should be able to point to just wit/ and then name whatever world it needs.

view this post on Zulip Alex Crichton (Mar 11 2026 at 14:43):

oh dear apologies I was also the one misinterpreting!

view this post on Zulip Alex Crichton (Mar 11 2026 at 14:48):

Is this the way I should share types between my packages?

Perhaps:

wit/
  something.wit       # embedder stuff? unsure?
  deps/
    projection.wit    # package umari:projection;
    command.wit       # package umari:command;
    types.wit         # package umari:types;

In general though this looks like it's better for a single package with interfaces in the package, and you can have multiple *.wit files make up one package.

would be interested in hearing about its reasoning

Basically just doing our best to design a system when there were no users at the time and now that we have lots of users there's much better-formed opinions but continued lack of resources to change things alas

I guess the macros don't support a wit directory with multiple packages defined?

If you'd like, there's more info on filesystem structure here too


Last updated: Mar 23 2026 at 16:19 UTC