Improve Rust ergonomics for wasip3 async main functions
It would be nice to write this kind of program:
extern crate wasi;
async fn main() {
let one_millisecond = 1_000_000u64;
wasi::clocks::monotonic_clock::wait_for(one_millisecond).await
}
However, there are a number of things that get in the way. Right now you need something like:
extern crate wit_bindgen;
wit_bindgen::generate!({
inline: r"
package test:test;
world test {
include wasi:clocks/[email protected];
include wasi:cli/[email protected];
}
",
// Work around https://github.com/bytecodealliance/wasm-tools/issues/2285.
features:["clocks-timezone"],
async: [
"wasi:cli/[email protected]#run",
],
generate_all
});
struct Component;
export!(Component);
impl exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
wasi::clocks::monotonic_clock::wait_for(one_millisecond).await
Ok(())
}
}
fn main() { unreachable!("main is a stub"); }
Which:
- It's weird to have the stub main function
- It would be nice to have the same
impl std::process::Terminationsetup forrunso we don't have toOK(()) - Would be nice to have the
wasi::cli::run::Guestimplementation produced automatically and just call theasync fn main() - Would be nice to avoid having to explicitly declare
wasi:cli/runas async when definingasync fn main()
Alex mentioned that something like this might be possible, and it seems 95% of the way there:
#[wit_bindgen::async]
async fn main() {}
I've transferred this issue to the wasi-rs repository as this is where the implementation would live, if any.
As of now this is the current baseline:
wasip3::cli::command::export!(Example);
struct Example;
impl wasip3::exports::cli::run::Guest for Example {
async fn run() -> Result<(), ()> {
let (mut tx, rx) = wasip3::wit_stream::new();
wasip3::cli::stdout::set_stdout(rx);
let remaining = tx.write_all(b"Hello, WASI!".to_vec()).await;
assert!(remaining.is_empty());
Ok(())
}
}
I still think it would be worthwhile to try to change that to:
#[wasip3::main]
async fn main() {
let (mut tx, rx) = wasip3::wit_stream::new();
wasip3::cli::stdout::set_stdout(rx);
let remaining = tx.write_all(b"Hello, WASI!".to_vec()).await;
assert!(remaining.is_empty());
}
Attribute macro_rules appears to be moving towards stabilization https://github.com/rust-lang/rust/issues/143547, with which we could probably manage #[wasip3::cli::run::export] async fn main() {...} for exported interfaces that have just one function. I'm assuming we can't use a proc macro because it has to be possible to use the implementation created by bindgen in the same crate it is defined.