I also did this recently but with the shim approach: a small Rust program that needs to run a cryptography operation on some input[1], compiled to wasi and operating on stdin/stdout, and then invoked by a larger Typescript program invoking it with the proper stream. Worked great.
There is also a thing called workers-rs[2] which will let you write the entire worker in Rust, but it does not use WASI. WASI isn't really enough for anything beyond simple stdin/stdout pipelines without something "wrapping it" to provide the needed fds, env vars, arguments, etc. So instead workers-rs binds to the underlying Worker runtime primitives, the ones normal JavaScript uses, and exposes that -- but it does all of the mangling/bindgen/shim bullshit for you in the background. The shim is unavoidable, and always exists behind the curtain, but this approach lets you completely ignore it. workers-rs doesn't support every API, though (e.g. no R2 support.) In theory, assuming you ported the workers-wasi interface to Rust[3] yourself, you could then write a Worker in Rust using workers-rs that load WASI-conformant WASM programs (written in XYZ) and execute that program on the request object. Sounds confusing but I think you get what I mean.
[1] No, this operation wasn't provided by the WebCrypto API, as far as I'm aware, otherwise I probably wouldn't have bothered with the added complexity.
[2] https://github.com/cloudflare/workers-rs
[3] The underlying WebAssembly support comes from V8; WASI, then, is "just" an implementation of some very well known functions/APIs in the surrounding environment that the WASM program expects to exist, and behave in a certain way. So actually the entire workers-wasi framework is here, and if you ported this TypeScript interface to Rust, using workers-rs, you could invoke any WASI program similarly: https://github.com/cloudflare/workers-wasi/blob/main/src/ind...
As far as I know, CFW heavily leans on V8 isolates for scaling.
Why reinvent the wheel?