Rust Calls from JavaScript

Background

Sync Rust calls

  • The generated JavaScript code calls UniffiScaffolding.callSync().

    • The first argument to callSync() is a numeric identifier for the FFI call, known to both the JavaScript and C++ code generators.

    • That is followed by lowered argument from the JavaScript call.

  • The generated C++ code then performs the second phase of argument lowering and makes the Rust call. This code is statically linked to the Rust code, so it can make this call directly.

  • The C++ code then returns a UniFFIScaffoldingCallResult to the JavaScript code, which is essentially a UniffiCallStatus plus a return value.

  • The generated JavaScript code inspects the UniFFIScaffoldingCallResult and either returns a value or raises an exception.

Wrapped-async calls

  • UniffiScaffolding.callAsyncWrapper is called instead of UniffiScaffolding.callSync.

  • The generated C++ code schedules the Rust call in a worker thread.

  • The generated C++ code returns a Promise to JavaScript

  • The generated C++ code resolves that promise using the returned Rust value.

  • The C++ code resolves the promise with a UniFFIScaffoldingCallResult value.

Async calls

  • Use UniffiScaffolding.callAsync() is used to make the Rust call

  • The generated C++ code returns a Promise to JavaScript

  • The generated C++ code implements the future callback and polls the Rust function as described in the UniFFI async overview.

  • Once the Rust future is complete, the C++ code resolves the JavaScript promise with the result

  • The C++ code resolves the promise with a UniFFIScaffoldingCallResult value.

Note: The generated code does not handle cancellation or the foreign_future_dropped_callback. In JavaScript once an async task has started running, there’s no way to force it to stop. Other JavaScript bindings have handled this by passing an Abort Controller as an extra argument to async functions, maybe we could also do this in the future.