I have this simple Rust function:
#[no_mangle]
pub fn compute(operator: &str, n1: i32, n2: i32) -> i32 {
match operator {
"SUM" => n1 + n2,
"DIFF" => n1 - n2,
"MULT" => n1 * n2,
"DIV" => n1 / n2,
_ => 0
}
}
I am compiling this to WebAssembly successfully, but don't manage to pass the operator
parameter from JS to Rust.
The JS line which calls the Rust function looks like this:
instance.exports.compute(operator, n1, n2);
operator
is a JS String
and n1
, n2
are JS Number
s.
n1
and n2
are passed properly and can be read inside the compiled function so I guess the problem is how I pass the string around. I imagine it is passed as a pointer from JS to WebAssembly but can't find evidence or material about how this works.
I am not using Emscripten and would like to keep it standalone (compilation target wasm32-unknown-unknown
), but I see they wrap their compiled functions in Module.cwrap
, maybe that could help?
As pointed out by Shepmaster, only numbers can be passed to WebAssembly, so we need to convert the string into an
Uint16Array
.To do so we can use this
str2ab
function found here:This now works:
Because we're passing a reference to an array of unsigned integers.
To transfer string data between JavaScript and Rust, you need to decide
Solution 1
I decided:
TextEncoder
JS API is the best fit.lib/src.rs
It's important to build C dylibs for WASM to help them be smaller in size.
Cargo.toml
For what it's worth, I'm running this code in Node, not in the browser.
index.js
Solution 2
I decided:
TextEncoder
JS API is the best fit.Box<String>
as the underlying data structure. This allows the allocation to be further used by Rust code.src/lib.rs
index.js
Note that this process can be used for other types. You "just" have to decide how to represent data as a set of bytes that both sides agree on then send it across.
See also:
TextEncoder
APIUint8Array
/Uint32Array
/TypedArray
WebAssembly.Memory
A WebAssembly program has it's own memory space. And this space is often managed by the WebAssembly program itself, with the help of an allocator library, such as the wee_alloc.
The JavaScript can see and modify that memory space, but it has no way of knowing how the allocator library structures are organized. So if we simply write to the WASM memory from the JavaScript then we'll likely overwrite something important and mess things up. Therefore the WebAssembly program itself must allocate the memory region first, pass it to JavaScript, and then the JavaScript can fill that region with the data.
In the following example we do just that: allocate a buffer in the WASM memory space, copy the UTF-8 bytes there, pass the buffer location to a Rust function, then free the buffer.
Rust:
TypeScript:
P.S. The
Module._malloc
in Emscripten might be semantically equivalent to thealloc
function we implemented above. Under the "wasm32-unknown-emscripten" target you can use theModule._malloc
with Rust.