Hi, I want to introduce host bindings to my project instead of manually setting and calling functions from components. making this change introduced some obstacles. Before I was able to use the Rust types by having them implement Component Model and either/both Lift or/and Lower:
#[derive(Debug, ComponentType, Lift, Serialize)]
#[component(record)]
pub(crate) struct Event {
id: String,
content: Vec<u8>,
}
world.wit
record event {
id: string,
content: byte-array,
}
Now using the host bindings from bindgen!, there is this type that implements Lower, Lift, ComponentType, etc.:
#[component(record)]
pub struct Event {
#[component(name = "id")]
pub id: wasmtime::component::__internal::String,
#[component(name = "content")]
pub content: ByteArray,
}
I thought that in my code (a simplified version) the following would work:
export execute: func()-> result<event>
fn exexute_in_component()-> wasmtime::Result<Result<Event, ()>>{
// --- some code ---
let instance = Guest::instanciate(...);
let result: Result<Result<bindings::host::Event, ()>, anyhow::Error> = instance::call_execute();
result
// prompts the error from the next example
}
but direct conversion is not allowed by the compiler:
let a: Event = bindings::host::Event{id: "qwerty".to_string(), content: Vec::new()};
// expected `persistence::Event`, found `bindings::host::Event`
// = note: `bindings::host::Event` and `persistence::Event` have similar names, but are actually distinct types
Even though, Event implements ComponentType and Lift, required for creating Rust types from Wit -Lift's doc: "Host types which can be created from the canonical ABI", the two types cannot be interchanged when converting from wit record to Rust struct. Of course, I can make an impl Into<Event> for host::Event implementation, but it introduces additional complexity to the code, because of nesting in the function's return type. I can go back to working with raw Instance and manually set the exports and imports, but I am sure there is a more elegant approach to using the host bindings.
This may not be implemented yet, but this is partially motivation for the with option to the bindgen! macro. I know that for resources you can specify a custom type, but I don't think we have it set up to specify a custom type for any arbitrary WIT type yet. Adding that in theory wouldn't be too hard though.
Then I am curious how manually defining and calling exports with Rust types which comply with trait bounds is working :
// My old approach by only using Rust structs
// type ByteArr = Vec<u8>
async fn call_execute(
&mut self,
cmd: PlatformMessage,
) -> Result<Result<Vec<Event>, ByteArr>, wasmtime::Error> {
self.load_instance(&cmd).await.and_then(|instance| {
instance
.get_typed_func::<(ByteArr,), (Result<Vec<Event>, ByteArr>,)>(
&mut self.store,
"execute",
)
.and_then(|func| func.call(&mut self.store, (cmd.payload().to_vec(),)))
.map(|result| result.0) // getting the data from (A,) to A
})
}
Maybe it is all about giving types, which comply with the trait bounds in .get_typed_func and wasmtime handles the type compatibility instead of the Rust compiler when using the host bindings
yeah the types you provide to get_typed_func are what the function ends up actually returning
there's still type-checks and such to make sure everything works out, but that's the part that has to agree
Thanks, then I will work with what is available. I am looking forward to seeing such type resolving in the future! If anybody has any workarounds feel free to share
Last updated: Dec 06 2025 at 06:05 UTC