Passing Rust Closures to JS (original) (raw)
- Introduction
- 1. Examples
- 1.1. Hello, World!
- 1.2. Using console.log
- 1.3. Small Wasm files
- 1.4. Without a Bundler
- 1.5. Synchronous Instantiation
- 1.6. Importing functions from JS
- 1.7. Working with char
- 1.8. js-sys: WebAssembly in WebAssembly
- 1.9. web-sys: DOM hello world
- 1.10. web-sys: Closures
- 1.11. web-sys: performance.now
- 1.12. web-sys: using fetch
- 1.13. web-sys: Weather report
- 1.14. web-sys: canvas hello world
- 1.15. web-sys: canvas Julia set
- 1.16. web-sys: WebAudio
- 1.17. web-sys: WebGL
- 1.18. web-sys: WebSockets
- 1.19. web-sys: WebRTC DataChannel
- 1.20. web-sys: requestAnimationFrame
- 1.21. web-sys: A Simple Paint Program
- 1.22. web-sys: Wasm in Web Worker
- 1.23. Parallel Raytracing
- 1.24. Wasm Audio Worklet
- 1.25. web-sys: A TODO MVC App
- 2. Reference
- 2.1. Deployment
- 2.2. JS snippets
- 2.3. Static JS Objects
- 2.4. Passing Rust Closures to JS
- 2.5. Receiving JS Closures in Rust
- 2.6. Promises and Futures
- 2.7. Iterating over JS Values
- 2.8. Arbitrary Data with Serde
- 2.9. Accessing Properties of Untyped JS Values
- 2.10. Working with Duck-Typed Interfaces
- 2.11. Command Line Interface
- 2.12. Optimizing for Size
- 2.13. Supported Rust Targets
- 2.14. Supported Browsers
- 2.15. Support for Weak References
- 2.16. Support for Reference Types
- 2.17. Supported Types
- 2.18. #[wasm_bindgen] Attributes
- 2.18.1. On JavaScript Imports
- 2.18.1.1. catch
- 2.18.1.2. constructor
- 2.18.1.3. extends
- 2.18.1.4. getter and setter
- 2.18.1.5. final
- 2.18.1.6. indexing_getter, indexing_setter, and indexing_deleter
- 2.18.1.7. js_class = "Blah"
- 2.18.1.8. js_name
- 2.18.1.9. js_namespace
- 2.18.1.10. method
- 2.18.1.11. module = "blah"
- 2.18.1.12. raw_module = "blah"
- 2.18.1.13. no_deref
- 2.18.1.14. static_method_of = Blah
- 2.18.1.15. structural
- 2.18.1.16. typescript_type
- 2.18.1.17. variadic
- 2.18.1.18. vendor_prefix
- 2.18.1.1. catch
- 2.18.2. On Rust Exports
- 2.18.2.1. constructor
- 2.18.2.2. js_name = Blah
- 2.18.2.3. js_class = Blah
- 2.18.2.4. readonly
- 2.18.2.5. skip
- 2.18.2.6. skip_jsdoc
- 2.18.2.7. start
- 2.18.2.8. main
- 2.18.2.9. typescript_custom_section
- 2.18.2.10. getter and setter
- 2.18.2.11. inspectable
- 2.18.2.12. skip_typescript
- 2.18.2.13. getter_with_clone
- 2.18.2.14. unchecked_return_type and unchecked_param_type
- 2.18.2.15. return_description and param_description
- 2.18.2.1. constructor
- 3. web-sys
- 4. Testing with wasm-bindgen-test
- 5. Contributing to wasm-bindgen
- 5.2. Internal Design
- 5.3. js-sys
- 5.4. web-sys
- 5.5. Publishing
- 5.6. Team
The `wasm-bindgen` Guide
Passing Rust Closures to Imported JavaScript Functions
The #[wasm_bindgen]
attribute supports Rust closures being passed to JavaScript in two variants:
- Stack-lifetime closures that should not be invoked by JavaScript again after the imported JavaScript function that the closure was passed to returns.
- Heap-allocated closures that can be invoked any number of times, but must be explicitly deallocated when finished. Stack-Lifetime Closures
Closures with a stack lifetime are passed to JavaScript as either &dyn Fn
or &mut dyn FnMut
trait objects:
# #![allow(unused_variables)]
#fn main() {
// Import JS functions that take closures
#[wasm_bindgen]
extern "C" {
fn takes_immutable_closure(f: &dyn Fn());
fn takes_mutable_closure(f: &mut dyn FnMut());
}
// Usage
takes_immutable_closure(&|| {
// ...
});
let mut times_called = 0;
takes_mutable_closure(&mut || {
times_called += 1;
});
#}
Once these imported functions return, the closures that were given to them will become invalidated, and any future attempts to call those closures from JavaScript will raise an exception.
Closures also support arguments and return values like exports do, for example:
# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
extern "C" {
fn takes_closure_that_takes_int_and_returns_string(x: &dyn Fn(u32) -> String);
}
takes_closure_that_takes_int_and_returns_string(&|x: u32| -> String {
format!("x is {}", x)
});
#}
Sometimes the discipline of stack-lifetime closures is not desired. For example, you'd like to schedule a closure to be run on the next turn of the event loop in JavaScript through setTimeout
. For this, you want the imported function to return but the JavaScript closure still needs to be valid!
For this scenario, you need the Closure
type, which is defined in thewasm_bindgen
crate, exported in wasm_bindgen::prelude
, and represents a "long lived" closure.
The validity of the JavaScript closure is tied to the lifetime of the Closure
in Rust. Once a Closure
is dropped, it will deallocate its internal memory and invalidate the corresponding JavaScript function so that any further attempts to invoke it raise an exception.
Like stack closures a Closure
supports both Fn
and FnMut
closures, as well as arguments and returns.
# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
extern "C" {
fn setInterval(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;
fn clearInterval(token: f64);
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub struct Interval {
closure: Closure<dyn FnMut()>,
token: f64,
}
impl Interval {
pub fn new<F: 'static>(millis: u32, f: F) -> Interval
where
F: FnMut()
{
// Construct a new closure.
let closure = Closure::new(f);
// Pass the closure to JS, to run every n milliseconds.
let token = setInterval(&closure, millis);
Interval { closure, token }
}
}
// When the Interval is destroyed, clear its `setInterval` timer.
impl Drop for Interval {
fn drop(&mut self) {
clearInterval(self.token);
}
}
// Keep logging "hello" every second until the resulting `Interval` is dropped.
#[wasm_bindgen]
pub fn hello() -> Interval {
Interval::new(1_000, || log("hello"))
}
#}