Passing Rust Closures to JS (original) (raw)

  1. Introduction
  2. 1. Examples
    1. 1.1. Hello, World!
    2. 1.2. Using console.log
    3. 1.3. Small Wasm files
    4. 1.4. Without a Bundler
    5. 1.5. Synchronous Instantiation
    6. 1.6. Importing functions from JS
    7. 1.7. Working with char
    8. 1.8. js-sys: WebAssembly in WebAssembly
    9. 1.9. web-sys: DOM hello world
    10. 1.10. web-sys: Closures
    11. 1.11. web-sys: performance.now
    12. 1.12. web-sys: using fetch
    13. 1.13. web-sys: Weather report
    14. 1.14. web-sys: canvas hello world
    15. 1.15. web-sys: canvas Julia set
    16. 1.16. web-sys: WebAudio
    17. 1.17. web-sys: WebGL
    18. 1.18. web-sys: WebSockets
    19. 1.19. web-sys: WebRTC DataChannel
    20. 1.20. web-sys: requestAnimationFrame
    21. 1.21. web-sys: A Simple Paint Program
    22. 1.22. web-sys: Wasm in Web Worker
    23. 1.23. Parallel Raytracing
    24. 1.24. Wasm Audio Worklet
    25. 1.25. web-sys: A TODO MVC App
  3. 2. Reference
    1. 2.1. Deployment
    2. 2.2. JS snippets
    3. 2.3. Static JS Objects
    4. 2.4. Passing Rust Closures to JS
    5. 2.5. Receiving JS Closures in Rust
    6. 2.6. Promises and Futures
    7. 2.7. Iterating over JS Values
    8. 2.8. Arbitrary Data with Serde
    9. 2.9. Accessing Properties of Untyped JS Values
    10. 2.10. Working with Duck-Typed Interfaces
    11. 2.11. Command Line Interface
    12. 2.12. Optimizing for Size
    13. 2.13. Supported Rust Targets
    14. 2.14. Supported Browsers
    15. 2.15. Support for Weak References
    16. 2.16. Support for Reference Types
    17. 2.17. Supported Types
      1. 2.17.1. Imported JavaScript Types
      2. 2.17.2. Exported Rust Types
      3. 2.17.3. JsValue
      4. 2.17.4. Box<[T]> and Vec
      5. 2.17.5. *const T and *mut T
      6. 2.17.6. NonNull
      7. 2.17.7. Numbers
      8. 2.17.8. bool
      9. 2.17.9. char
      10. 2.17.10. str
      11. 2.17.11. String
      12. 2.17.12. Number Slices
      13. 2.17.13. Boxed Number Slices
      14. 2.17.14. Result<T, E>
    18. 2.18. #[wasm_bindgen] Attributes
      1. 2.18.1. On JavaScript Imports
        1. 2.18.1.1. catch
          1. 2.18.1.2. constructor
          2. 2.18.1.3. extends
          3. 2.18.1.4. getter and setter
          4. 2.18.1.5. final
          5. 2.18.1.6. indexing_getter, indexing_setter, and indexing_deleter
          6. 2.18.1.7. js_class = "Blah"
          7. 2.18.1.8. js_name
          8. 2.18.1.9. js_namespace
          9. 2.18.1.10. method
          10. 2.18.1.11. module = "blah"
          11. 2.18.1.12. raw_module = "blah"
          12. 2.18.1.13. no_deref
          13. 2.18.1.14. static_method_of = Blah
          14. 2.18.1.15. structural
          15. 2.18.1.16. typescript_type
          16. 2.18.1.17. variadic
          17. 2.18.1.18. vendor_prefix
      2. 2.18.2. On Rust Exports
        1. 2.18.2.1. constructor
          1. 2.18.2.2. js_name = Blah
          2. 2.18.2.3. js_class = Blah
          3. 2.18.2.4. readonly
          4. 2.18.2.5. skip
          5. 2.18.2.6. skip_jsdoc
          6. 2.18.2.7. start
          7. 2.18.2.8. main
          8. 2.18.2.9. typescript_custom_section
          9. 2.18.2.10. getter and setter
          10. 2.18.2.11. inspectable
          11. 2.18.2.12. skip_typescript
          12. 2.18.2.13. getter_with_clone
          13. 2.18.2.14. unchecked_return_type and unchecked_param_type
          14. 2.18.2.15. return_description and param_description
  4. 3. web-sys
    1. 3.1. Using web-sys
    2. 3.2. Cargo Features
    3. 3.3. Function Overloads
    4. 3.4. Type Translations
    5. 3.5. Inheritance
    6. 3.6. Unstable APIs
  5. 4. Testing with wasm-bindgen-test
    1. 4.1. Usage
    2. 4.2. Writing Asynchronous Tests
    3. 4.3. Testing in Headless Browsers
    4. 4.4. Continuous Integration
    5. 4.5. Coverage (Experimental)
  6. 5. Contributing to wasm-bindgen
    1. 5.1. Testing
  7. 5.2. Internal Design
    1. 5.2.1. JS Objects in Rust
      1. 5.2.2. Exporting a function to JS
      2. 5.2.3. Exporting a struct to JS
      3. 5.2.4. Importing a function from JS
      4. 5.2.5. Importing a class from JS
      5. 5.2.6. Rust Type conversions
      6. 5.2.7. Types in wasm-bindgen
  8. 5.3. js-sys
    1. 5.3.1. Testing
      1. 5.3.2. Adding More APIs
  9. 5.4. web-sys
    1. 5.4.1. Overview
      1. 5.4.2. Testing
      2. 5.4.3. Logging
      3. 5.4.4. Supporting More Web APIs
  10. 5.5. Publishing
  11. 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:

  1. Stack-lifetime closures that should not be invoked by JavaScript again after the imported JavaScript function that the closure was passed to returns.
  2. 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)
});
#}

Heap-Allocated Closures

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 Closurein 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"))
}
#}