[−][src]Struct wasm_bindgen::closure::Closure
A handle to both a closure in Rust as well as JS closure which will invoke the Rust closure.
A Closure
is the primary way that a 'static
lifetime closure is
transferred from Rust to JS. Closure
currently requires that the closures
it's created with have the 'static
lifetime in Rust for soundness reasons.
This type is a "handle" in the sense that whenever it is dropped it will
invalidate the JS closure that it refers to. Any usage of the closure in JS
after the Closure
has been dropped will raise an exception. It's then up
to you to arrange for Closure
to be properly deallocate at an appropriate
location in your program.
The type parameter on Closure
is the type of closure that this represents.
Currently this can only be the Fn
and FnMut
traits with up to 7
arguments (and an optional return value). The arguments/return value of the
trait must be numbers like u32
for now, although this restriction may be
lifted in the future!
Examples
Here are a number of examples of using Closure
.
Using the setInterval
API
Sample usage of Closure
to invoke the setInterval
API.
use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { fn setInterval(closure: &Closure<FnMut()>, time: u32) -> i32; fn clearInterval(id: i32); #[wasm_bindgen(js_namespace = console)] fn log(s: &str); } #[wasm_bindgen] pub struct IntervalHandle { interval_id: i32, _closure: Closure<FnMut()>, } impl Drop for IntervalHandle { fn drop(&mut self) { clearInterval(self.interval_id); } } #[wasm_bindgen] pub fn run() -> IntervalHandle { // First up we use `Closure::wrap` to wrap up a Rust closure and create // a JS closure. let cb = Closure::wrap(Box::new(|| { log("interval elapsed!"); }) as Box<FnMut()>); // Next we pass this via reference to the `setInterval` function, and // `setInterval` gets a handle to the corresponding JS closure. let interval_id = setInterval(&cb, 1_000); // If we were to drop `cb` here it would cause an exception to be raised // whenever the interval elapses. Instead we *return* our handle back to JS // so JS can decide when to cancel the interval and deallocate the closure. IntervalHandle { interval_id, _closure: cb, } }
Casting a Closure
to a js_sys::Function
This is the same setInterval
example as above, except it is using
web_sys
(which uses js_sys::Function
for callbacks) instead of manually
writing bindings to setInterval
and other Web APIs.
use wasm_bindgen::JsCast; #[wasm_bindgen] pub struct IntervalHandle { interval_id: i32, _closure: Closure<FnMut()>, } impl Drop for IntervalHandle { fn drop(&mut self) { let window = web_sys::window().unwrap(); window.clear_interval_with_handle(self.interval_id); } } #[wasm_bindgen] pub fn run() -> Result<IntervalHandle, JsValue> { let cb = Closure::wrap(Box::new(|| { web_sys::console::log_1(&"inverval elapsed!".into()); }) as Box<FnMut()>); let window = web_sys::window().unwrap(); let interval_id = window.set_interval_with_callback_and_timeout_and_arguments_0( // Note this method call, which uses `as_ref()` to get a `JsValue` // from our `Closure` which is then converted to a `&Function` // using the `JsCast::unchecked_ref` function. cb.as_ref().unchecked_ref(), 1_000, )?; // Same as above. Ok(IntervalHandle { interval_id, _closure: cb, }) }
Using FnOnce
and Closure::once
with requestAnimationFrame
Because requestAnimationFrame
only calls its callback once, we can use
FnOnce
and Closure::once
with it.
use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { fn requestAnimationFrame(closure: &Closure<FnMut()>) -> u32; fn cancelAnimationFrame(id: u32); #[wasm_bindgen(js_namespace = console)] fn log(s: &str); } #[wasm_bindgen] pub struct AnimationFrameHandle { animation_id: u32, _closure: Closure<FnMut()>, } impl Drop for AnimationFrameHandle { fn drop(&mut self) { cancelAnimationFrame(self.animation_id); } } // A type that will log a message when it is dropped. struct LogOnDrop(&'static str); impl Drop for LogOnDrop { fn drop(&mut self) { log(self.0); } } #[wasm_bindgen] pub fn run() -> AnimationFrameHandle { // We are using `Closure::once` which takes a `FnOnce`, so the function // can drop and/or move things that it closes over. let fired = LogOnDrop("animation frame fired or canceled"); let cb = Closure::once(move || drop(fired)); // Schedule the animation frame! let animation_id = requestAnimationFrame(&cb); // Again, return a handle to JS, so that the closure is not dropped // immediately and JS can decide whether to cancel the animation frame. AnimationFrameHandle { animation_id, _closure: cb, } }
Converting FnOnce
s directly into JavaScript Functions with Closure::once_into_js
If we don't want to allow a FnOnce
to be eagerly dropped (maybe because we
just want it to drop after it is called and don't care about cancellation)
then we can use the Closure::once_into_js
function.
This is the same requestAnimationFrame
example as above, but without
supporting early cancellation.
use wasm_bindgen::prelude::*; #[wasm_bindgen] extern "C" { // We modify the binding to take an untyped `JsValue` since that is what // is returned by `Closure::once_into_js`. // // If we were using the `web_sys` binding for `requestAnimationFrame`, // then the call sites would cast the `JsValue` into a `&js_sys::Function` // using `f.unchecked_ref::<js_sys::Function>()`. See the `web_sys` // example above for details. fn requestAnimationFrame(callback: JsValue); #[wasm_bindgen(js_namespace = console)] fn log(s: &str); } // A type that will log a message when it is dropped. struct LogOnDrop(&'static str); impl Drop for LogOnDrop { fn drop(&mut self) { log(self.0); } } #[wasm_bindgen] pub fn run() { // We are using `Closure::once_into_js` which takes a `FnOnce` and // converts it into a JavaScript function, which is returned as a // `JsValue`. let fired = LogOnDrop("animation frame fired"); let cb = Closure::once_into_js(move || drop(fired)); // Schedule the animation frame! requestAnimationFrame(cb); // No need to worry about whether or not we drop a `Closure` // here or return some sort of handle to JS! }
Methods
impl<T: ?Sized> Closure<T> where
T: WasmClosure,
[src]
T: WasmClosure,
pub fn wrap(data: Box<T>) -> Closure<T>
[src]
Creates a new instance of Closure
from the provided boxed Rust
function.
Note that the closure provided here, Box<T>
, has a few requirements
associated with it:
-
It must implement
Fn
orFnMut
(forFnOnce
functions seeClosure::once
andClosure::once_into_js
). -
It must be
'static
, aka no stack references (use themove
keyword). -
It can have at most 7 arguments.
-
Its arguments and return values are all types that can be shared with JS (i.e. have
#[wasm_bindgen]
annotations or are simple numbers, etc.)
pub fn forget(self)
[src]
Leaks this Closure
to ensure it remains valid for the duration of the
entire program.
Note: this function will leak memory. It should be used sparingly to ensure the memory leak doesn't affect the program too much.
When a Closure
is dropped it will invalidate the associated JS
closure, but this isn't always desired. Some callbacks are alive for
the entire duration of the program, so this can be used to conveniently
leak this instance of Closure
while performing as much internal
cleanup as it can.
impl Closure<dyn FnOnce()>
[src]
pub fn once<F, A, R>(fn_once: F) -> Closure<F::FnMut> where
F: 'static + WasmClosureFnOnce<A, R>,
[src]
F: 'static + WasmClosureFnOnce<A, R>,
Create a Closure
from a function that can only be called once.
Since we have no way of enforcing that JS cannot attempt to call this
FnOne(A...) -> R
more than once, this produces a Closure<FnMut(A...) -> R>
that will dynamically throw a JavaScript error if called more
than once.
Example
use wasm_bindgen::prelude::*; // Create an non-`Copy`, owned `String`. let mut s = String::from("Hello"); // Close over `s`. Since `f` returns `s`, it is `FnOnce` and can only be // called once. If it was called a second time, it wouldn't have any `s` // to work with anymore! let f = move || { s += ", World!"; s }; // Create a `Closure` from `f`. Note that the `Closure`'s type parameter // is `FnMut`, even though `f` is `FnOnce`. let closure: Closure<FnMut() -> String> = Closure::once(f);
pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue where
F: 'static + WasmClosureFnOnce<A, R>,
[src]
F: 'static + WasmClosureFnOnce<A, R>,
Convert a FnOnce(A...) -> R
into a JavaScript Function
object.
If the JavaScript function is invoked more than once, it will throw an exception.
Unlike Closure::once
, this does not return a Closure
that can be
dropped before the function is invoked to deallocate the closure. The
only way the FnOnce
is deallocated is by calling the JavaScript
function. If the JavaScript function is never called then the FnOnce
and everything it closes over will leak.
use js_sys; use wasm_bindgen::{prelude::*, JsCast}; let f = Closure::once_into_js(move || { // ... }); assert!(f.is_instance_of::<js_sys::Function>());
Trait Implementations
impl<'a, T: ?Sized> IntoWasmAbi for &'a Closure<T> where
T: WasmClosure,
[src]
T: WasmClosure,
type Abi = u32
The wasm ABI type that this converts into when crossing the ABI boundary. Read more
fn into_abi(self, extra: &mut dyn Stack) -> u32
[src]
impl<T> Debug for Closure<T> where
T: ?Sized,
[src]
T: ?Sized,
impl<T> Drop for Closure<T> where
T: ?Sized,
[src]
T: ?Sized,
impl<T: ?Sized> AsRef<JsValue> for Closure<T>
[src]
Auto Trait Implementations
Blanket Implementations
impl<T> From for T
[src]
impl<T, U> TryFrom for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,
type Error = <U as TryFrom<T>>::Error
The type returned in the event of a conversion error.
fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>
[src]
impl<T, U> Into for T where
U: From<T>,
[src]
U: From<T>,
impl<T> Borrow for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut for T where
T: ?Sized,
[src]
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,