I created a small Wasm file from this Rust code:
#[no_mangle] pub fn hello() -> &'static str { "hello from rust" }
It builds and the hello
function can be called from JS:
<!DOCTYPE html> <html> <body> <script> fetch('main.wasm') .then(response => response.arrayBuffer()) .then(bytes => WebAssembly.instantiate(bytes, {})) .then(results => { alert(results.instance.exports.hello()); }); </script> </body> </html>
My problem is that the alert
displays "undefined". If I return a i32
, it works and displays the i32
. I also tried to return a String
but it does not work (it still displays "undefined").
Is there a way to return a string from Rust in WebAssembly? What type should I use?
The State of WebAssembly 2022 survey of Wasm developers shows Rust on top, with Blazor and Python on the rise. The Rust programming language is the most frequently used language for developing WebAssembly applications, according to a recent survey. And WebAssembly is growing in popularity.
In one recent study, a developer discovered Wasm is faster than JavaScript across three desktop browsers on desktop computers and smartphones. Wasm is 1.15-1.67 times faster than JavaScript on Google Chrome on a desktop. Wasm is 1.95-11.71 times faster than JavaScript on Firefox on a desktop.
The wasm-bindgen tool is sort of half polyfill for features like the host bindings proposal and half features for empowering high-level interactions between JS and wasm-compiled code (currently mostly from Rust).
Building the packageCompiles your Rust code to WebAssembly. Runs wasm-bindgen on that WebAssembly, generating a JavaScript file that wraps up that WebAssembly file into a module the browser can understand. Creates a pkg directory and moves that JavaScript file and your WebAssembly code into it. Reads your Cargo.
WebAssembly only supports a few numeric types, which is all that can be returned via an exported function.
When you compile to WebAssembly, your string will be held in the module's linear memory. In order to read this string from the hosting JavaScript, you need to return a reference to its location in memory, and the length of the string, i.e. two integers. This allows you to read the string from memory.
You use this same technique regardless of whichever language you are compiling to WebAssembly. How can I return a JavaScript string from a WebAssembly function provides a detailed background to the problem.
With Rust specifically, you need to make use of the Foreign Function Interface (FFI), using the CString
type as follows:
use std::ffi::CString; use std::os::raw::c_char; static HELLO: &'static str = "hello from rust"; #[no_mangle] pub fn get_hello() -> *mut c_char { let s = CString::new(HELLO).unwrap(); s.into_raw() } #[no_mangle] pub fn get_hello_len() -> usize { HELLO.len() }
The above code exports two functions, get_hello
which returns a reference to the string, and get_hello_len
which returns its length.
With the above code compiled to a wasm module, the string can be accessed as follows:
const res = await fetch('chip8.wasm'); const buffer = await res.arrayBuffer(); const module = await WebAssembly.compile(buffer); const instance = await WebAssembly.instantiate(module); // obtain the module memory const linearMemory = instance.exports.memory; // create a buffer starting at the reference to the exported string const offset = instance.exports.get_hello(); const stringBuffer = new Uint8Array(linearMemory.buffer, offset, instance.exports.get_hello_len()); // create a string from this buffer let str = ''; for (let i=0; i<stringBuffer.length; i++) { str += String.fromCharCode(stringBuffer[i]); } console.log(str);
The C equivalent can be seen in action in a WasmFiddle.
You cannot directly return a Rust String
or an &str
. Instead allocate and return a raw byte pointer containing the data which has to be then encoded as a JS string on the JavaScript side.
You can take a look at the SHA1 example here.
The functions of interest are in
demos/bundle.js
- copyCStr
demos/sha1/sha1-digest.rs
- digest
For more examples: https://www.hellorust.com/demos/sha1/index.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With