In addition to what @jonh mentioned (which may mean that this is a microbenchmark that wasm simply can't really do that much better on) your build script doesn't pass --release so you're testing a debug build of Rust instead of an optimized build.
i also think this is caused by the wasm↔js marshaling overhead; this puts a minimum on the granularity of work for which it is efficient to call out to webassembly
your function is likely performing too little work to be worth it
to know for sure: what happens if put a benchmarking loop inside the rust code?
seems relevant to this: for newer versions of Firefox this overhead is going to be reduced and "calls between JS and WebAssembly are faster than non-inlined JS to JS function calls"
To clarify, though, note that that only applies to the actual call from JS into WASM. With wasm-bindgen, if there are strings or other non-primitive types in a function signature, you're not calling WASM directly but going through a generated wrapper function that converts those types, and that work will be just as expensive as before.
thank you, yes i wondered about that, whether they optimized only the calls themselves or also special-cased argument copying—which is a large part of overhead with web-sys because so many DOM functions take strings—so apparently not (yet)!