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.
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....
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?
Both of your points were spot on! There's two add_to_linker functions:
wit::add_to_linker: root one I guess. Didn't work.wit::Projection::add_to_linker: world one? This was the correct one.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.
Ari Seyhun has marked this topic as resolved.
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.
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.
Hmm that won't work either, because ComponentRunStates needs to be Send too
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
@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
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.
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?
Ari Seyhun said:
I have another question: wit_bindgen doesn't seem to generate
Clonefor resources? I tried specifying it inadditional_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
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).
ah, sorry, misunderstood the context -- yes, I was speaking at the WIT API/type-system level
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
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".
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?
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?
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.
oh dear apologies I was also the one misinterpreting!
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