求与给定WAT等价的可运行Rust代码(解决导入错误)
问题与解决方案
问题背景
一段WAT代码嵌入Wasmtime宿主后运行正常,但对应的Rust代码编译运行时出现错误:
Running
target\debug\hello_world.exe
Error: expected 5 imports, found 1
error: process didn't exit successfully:target\debug\hello_world.exe(exit code: 1)
正常运行的WAT代码
(module (import "host" "greet" (func $greet (param i32 i32))) (func (export "run") i32.const 4 ;; ptr i32.const 22 ;; len call $greet) (memory (export "memory") 1) (data (i32.const 4) "Calling back from WAT!") )
报错的Rust代码
use std::ffi::CString; #[link(wasm_import_module = "host")] extern "C" { fn greet(ptr: i32, len: i32); } static GREETING: &str = "Calling back from Rust!"; #[no_mangle] pub extern "C" fn run() { let greeting = CString::new(GREETING).expect("contains null byte"); let ptr = greeting.as_ptr(); std::mem::forget(ptr); unsafe { greet(ptr as i32, GREETING.len() as i32); } }
宿主应用代码
use std::str; use anyhow::Result; use wasmtime::{Caller, Engine, Extern, Func, Instance, Module, Store}; struct MyState { name: String, count: usize, } fn main() -> Result<()> { let engine = Engine::default(); let code = include_bytes!("../hello.wat"); let module = Module::new(&engine, code)?; let mut store = Store::new( &engine, MyState { name: "Hello, junglie85!".to_string(), count: 0, }, ); let greet_func = Func::wrap( &mut store, |mut caller: Caller<'_, MyState>, ptr: i32, len: i32| { let mem = match caller.get_export("memory") { Some(Extern::Memory(mem)) => mem, _ => anyhow::bail!("failed to find host memory"), }; let data = mem .data(&caller) .get(ptr as u32 as usize..) .and_then(|arr| arr.get(..len as u32 as usize)); let string = match data { Some(data) => match str::from_utf8(data) { Ok(s) => s, Err(_) => anyhow::bail!("invalid utf-8"), }, None => anyhow::bail!("pointer/length out of bounds"), }; println!("> {} {}", caller.data().name, string); caller.data_mut().count += 1; Ok(()) }, ); let imports = [greet_func.into()]; let instance = Instance::new(&mut store, &module, &imports)?; let run = instance.get_typed_func::<(), ()>(&mut store, "run")?; println!("# Global count = {}", store.data().count); run.call(&mut store, ())?; println!("# Global count = {}", store.data().count); Ok(()) }
错误原因
默认情况下,Rust编译Wasm时会自动引入标准库的多个导入项(比如内存分配、panic处理、线程相关等),但你的宿主只提供了host.greet这一个导入,导致导入数量不匹配,触发错误。
要实现和WAT完全等价的Rust代码,需要消除这些额外的标准库依赖,手动对齐WAT的结构:导入指定函数、导出内存和run函数、将字符串写入固定内存位置。
等价的正确Rust代码
#![no_std] #![no_main] // 导入host模块的greet函数,和WAT的import声明完全对应 #[link(wasm_import_module = "host")] extern "C" { fn greet(ptr: i32, len: i32); } // 定义并导出1页大小的Wasm内存(1页=65536字节),对应WAT的(memory (export "memory") 1) #[link_section = "memory"] #[export_name = "memory"] static MEMORY: [u8; 65536] = [0; 65536]; // 将字符串写入内存偏移4的位置,对应WAT的(data (i32.const 4) "...") #[link_section = ".data..L0"] #[used] static GREETING: [u8; 23] = *b"Calling back from Rust!"; // 导出run函数,逻辑和WAT一致:传入固定指针和长度调用greet #[no_mangle] pub extern "C" fn run() { unsafe { greet(4, 23); // 23是"Calling back from Rust!"的字符长度 } } // 禁用标准库后必须提供panic处理函数(空实现即可,因为代码不会触发panic) #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
编译配置
在Cargo.toml中添加以下配置,确保编译出最小化、无额外依赖的Wasm模块:
[package] name = "hello_world" version = "0.1.0" edition = "2021" [profile.release] panic = "abort" opt-level = "z" lto = true strip = true [dependencies] # 无需额外依赖,因为我们禁用了标准库
编译命令:
cargo build --target wasm32-unknown-unknown --release
这段代码编译后的Wasm模块结构和你提供的WAT完全一致,只会导入host.greet,导出memory和run,字符串固定在内存偏移4的位置,能被宿主正常加载运行。
内容的提问来源于stack exchange,提问作者junglie85
相关产品推荐
相关产品推荐

