Hi all,
I have been struggling for some time on how to communicate from Host to Guest. Ultimately, I'd like for the Guest to be able to block on a message stream in its own thread so that the Host can send bytes/messages to the Guest. The Guest processes bytes/messages as the Host sends them.
I believe I have my wit directory figured out and am successfully generating both Host and Guest bindings. However, when I try to instantiate and call a function of the component, I am receiving errors due to mismatched types. I've tried to pare down the code to a minimal working example:
.wit:
package whisper:module@1.0.0;
world whisper-module {
use wasi:io/streams@0.2.6.{input-stream};
export run: func(message-stream: input-stream);
}
main.rs. Sorry it's a bit verbose
use std::error::Error;
use wasmtime::{Config, Engine, Store};
use wasmtime_wasi::{p2::{pipe::MemoryInputPipe, subscribe, IoView, WasiCtx, WasiCtxBuilder, WasiView}, DirPerms, FilePerms, ResourceTable};
wasmtime::component::bindgen!({
world: "whisper-module",
path: r#"..\wit"#,
});
pub struct MyState {
ctx: WasiCtx,
table: ResourceTable,
}
impl IoView for MyState {
fn table(&mut self) -> &mut ResourceTable { &mut self.table }
}
impl WasiView for MyState {
fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx }
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let data = std::fs::read(r".\target\wasm32-wasip2\debug\whisper_module_file_wasm.wasm").unwrap();
let mut config = Config::new();
config.async_support(true);
config.wasm_component_model(true);
let engine = Engine::new(&config)?;
let mut linker = wasmtime::component::Linker::<MyState>::new(&engine);
WhisperModule::add_to_linker(&mut linker, |state: &mut MyState| state)?;
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
let mut builder = WasiCtxBuilder::new();
let input_pipe = MemoryInputPipe::new(vec![]);
let mut resource_table = ResourceTable::new();
let resource = resource_table.push(input_pipe).unwrap();
let pollable = subscribe(&mut resource_table, resource).unwrap();
let mut store = Store::new(
&engine,
MyState {
ctx: builder
.inherit_network()
.inherit_stdio()
.preopened_dir(r#"C:\"#, r#"/"#, DirPerms::all(), FilePerms::all())?
.build(),
table: ResourceTable::new(),
},
);
let component = wasmtime::component::Component::new(&engine, data)?;
let module = WhisperModule::instantiate(&mut store, &component, &linker)?;
module.call_run(&mut store, resource);
Ok(())
}
The error I am receiving on the module.call_run line. I get:
mismatched types
expected struct `wasmtime::component::Resource<wasi::io::streams::InputStream>`
found struct `wasmtime::component::Resource<MemoryInputPipe>`
If I try to generalize it to a pollable, I get:
mismatched types
expected struct `wasmtime::component::Resource<wasi::io::poll::Pollable>`
found struct `wasmtime::component::Resource<DynPollable>`
My main questions:
MemoryInputPipe to work, because it implements InputStream. Why doesn't it?Thank you!
Okay, I somehow fumbled my way through it and have a tokio::io::duplex as an AsyncReadStream passed to the Guest. I still am not quite sure how everything works and ties together, but can make progress with this I think. If there are any good resources about how wit bindings translate into rust code on both the Host side and Guest side, I would greatly appreciate any references.
Thanks everyone!
Last updated: Dec 06 2025 at 06:05 UTC