Types of Parameters

These are all the types of parameters that can be sent over the JS-Rust bridge:

Most common for small JSON-serialized data (e.g. using Serde).
Create in JS"Hello, world!"
Type in RustString
Returned to JSString
Borrowing dataas_str() -> &str
Transferring ownershipinto_string() -> String
CaveatsData is copied when passed over the bridge.
Most common for large serialized data.
Create in JSzaplib.createReadOnlyBuffer(new Uint8Array([1, 2, 3]))
Type in RustArc<Vec<u8>>
Returned to JSZapTypedArray (extends Uint8Array)
Borrowing dataas_u8_slice() -> &[u8]
Adding ownership (refcount)as_arc_vec_u8() -> Arc<Vec<u8>>
CaveatsNo enforcement of read-only on JS side (yet).
Most common for graphics data.
Same as above, but instead with f32 and Float32Array.
Less common.
Create in JSzaplib.createMutableBuffer(new Uint8Array([1, 2, 3]))
Type in RustVec<u8>
Returned to JSZapTypedArray (extends Uint8Array)
Borrowing dataas_u8_slice() -> &[u8]
Transferring ownershipinto_vec_u8() -> Vec<u8>
CaveatsOnce passed from JS to Rust, the data cannot be used on the JS side any more (neither reading nor writing); representing transfer of ownership to Rust. This is not enforced (yet).
Less common.
Same as above, but instead with f32 and Float32Array.

As noted in the caveats above, you must take care when using these buffers on the JavaScript side:

  • Read-only buffers should not be mutated in JS. If you do mutate them anyway, race conditions and data corruption can occur. This restriction is not enforced (yet).
  • Mutable buffers can be mutated in JS. However, once you pass a mutable buffer into Rust, you cannot use the buffer in JS in any way. This is because ownership is passed to Rust, which can now mutate the data. If you read from such a stale buffer in JS, race conditions and data corruption can occur. This restriction is not enforced (yet).
    • It is possible to mutate some data in JS, then in Rust, and then in JS again, without ever copying of the data. Just pass the mutable buffer back from Rust to JS when you're done with it.

When a u8 or f32 buffer is returned to JS, you get a ZapTypedArray:

  • This extends either Uint8Array or Float32Array.
  • This typed array is backed by the WebAssembly memory.
  • When the typed array gets garbage collected, the WebAssembly memory is updated accordingly (the refcount is decreased for read-only buffers; and the memory is freed for mutable buffers).
  • This does mean that if you want to pass such a typed array to a Web Worker, that you have to use zaplib.serializeZapArrayForPostMessage. If you don't, the data might get de- or re-allocated before you can use it.
    • This is enforced by monkey-patching postMessage when you call zaplib.initialize(), so don't worry too much about it.
  • It is possible to call subarray() on these typed arrays, and the garbage collection be tracked properly.
    • It is even possible to create a new typed array using new Uint8Array(zapArray.buffer, zapArray.byteOffset, zapArray.length), and the garbage collection will still be tracked properly!
    • This makes it possible to pass these typed arrays into most existing libraries.
    • However, it's not possible to pass a sub-slice of a typed array to Rust.

When sending small amounts of data in either direction, we recommend simply JSON-serializing the data and sending it as a string. On the Rust side, Serde is a fine library for this.

Futher note that when using Zapium, data is often copied anyway, even when in the WebAssembly version it is not. This is one of the reasons why we do not recommend using Zapium yet.