Skip to main content

ScopedClosure

Struct ScopedClosure 

Source
pub struct ScopedClosure<'a, T: ?Sized> { /* private fields */ }
Expand description

A closure with a lifetime parameter that represents a Rust closure passed to JavaScript.

ScopedClosure<'a, T> is the unified closure type. The lifetime 'a indicates how long the closure is valid:

  • ScopedClosure<'static, T> - An owned closure with heap-allocated data. Requires 'static captures. Use for long-lived closures like event listeners and timers. Created with Closure::new or ScopedClosure::own. May transfer ownership to the JS GC using finalizers.

  • ScopedClosure<'a, T> (non-'static) - A borrowed closure referencing stack data. Allows non-'static captures. Use for immediate/synchronous callbacks. Created with ScopedClosure::borrow (for FnMut) or [ScopedClosure::borrow_immutable] (for Fn). Cannot transfer ownership to JS GC.

Closure<T> is currently a type alias for ScopedClosure<'static, T>. In a future release, a lifetime argument will be added to this alias.

§Ownership Model

ScopedClosure follows the same ownership model as other wasm-bindgen types: the JavaScript reference remains valid until the Rust value is dropped. When dropped, the closure is invalidated and any subsequent calls from JavaScript will throw: “closure invoked recursively or after being dropped”.

For 'static closures, you can also:

  • Pass by value to transfer ownership to JS (implements IntoWasmAbi)
  • Call forget() to leak the closure (JS can use it indefinitely)
  • Call into_js_value() to transfer to JS GC management

§Lifetime Safety

For borrowed closures, Rust’s borrow checker ensures that ScopedClosure cannot be held longer than the closure’s captured data:

let mut sum = 0;
let mut f = |x: u32| { sum += x; };  // f borrows sum
let closure = ScopedClosure::borrow(&mut f);  // closure borrows f
// closure cannot outlive f, and f cannot outlive sum

§Examples

§Borrowed closures with ScopedClosure::borrow

Use for immediate/synchronous callbacks where JS calls the closure right away:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn call_immediately(cb: &ScopedClosure<dyn FnMut(u32)>);
}

let mut sum = 0;
{
    let mut f = |x: u32| { sum += x; };
    let closure = ScopedClosure::borrow(&mut f);
    call_immediately(&closure);
}  // closure dropped here, JS function invalidated
assert_eq!(sum, 42);

§Owned closures with Closure::new

Use for long-lived callbacks like event listeners and timers:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
}

// Closure::new requires 'static, so use `move` to capture by value
let cb = Closure::new(move || {
    // ...
});
setInterval(&cb, 1000);
// Must keep `cb` alive or call `cb.forget()` to transfer to JS

§Transferring ownership to JS

Pass a ScopedClosure<'static, T> by value to transfer ownership:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn set_one_shot_callback(cb: ScopedClosure<dyn FnMut()>);
}

let cb = ScopedClosure::own(|| { /* ... */ });
set_one_shot_callback(cb);  // Ownership transferred, no need to store

Implementations§

Source§

impl<'a, T> ScopedClosure<'a, T>
where T: ?Sized + WasmClosure,

Source

pub fn as_js_value(&self) -> &JsValue

Obtain a &JsValue reference for this closure

Source§

impl<T> ScopedClosure<'static, T>
where T: ?Sized + WasmClosure,

Methods for creating and managing 'static closures.

These methods are only available on ScopedClosure<'static, T> not on borrowed ScopedClosure<'a, T> where 'a is not 'static.

Source

pub fn new<F>(t: F) -> Self
where F: IntoWasmClosure<T> + 'static,

Creates a new owned 'static closure that aborts on panic.

Alias for own_aborting.

Note: Not unwind safe. Prefer own or own with AssertUnwindSafe when possible.

Source

pub fn own<F>(t: F) -> Self
where F: IntoWasmClosure<T> + MaybeUnwindSafe + 'static,

Creates a new static owned ScopedClosure<'static, T> from the provided Rust function, with panic unwind support.

The type parameter T determines whether the closure is Fn or FnMut.

Supports unwind via its UnwindSafe bound when building with panic=unwind.

When provided to a JS function as an own value, to be managed by the JS GC.

See borrow for creating a borrowed ScopedClosure with an associated lifetime (defaults to immutable Fn).

Source

pub fn own_aborting<F>(t: F) -> Self
where F: IntoWasmClosure<T> + 'static,

Creates a new owned 'static closure that aborts on panic.

Unlike own, this version does NOT catch panics and does NOT require UnwindSafe. If the closure panics, the process will abort.

The type parameter T determines whether the closure is Fn or FnMut.

When provided to a JS function as an own value, to be managed by the JS GC.

See borrow_aborting for creating a borrowed ScopedClosure with an associated lifetime.

Note: Not unwind safe. Prefer own or own with AssertUnwindSafe when possible.

Source

pub fn wrap<F>(data: Box<F>) -> Self
where F: IntoWasmClosure<T> + ?Sized,

A more direct version of Closure::new which creates a Closure from a Box<dyn Fn>/Box<dyn FnMut>, which is how it’s kept internally.

This version does NOT catch panics. If the closure panics, the process will abort.

Note: Not unwind safe. Prefer wrap_assert_unwind_safe when possible.

Source

pub fn wrap_assert_unwind_safe<F>(data: Box<F>) -> Self
where F: IntoWasmClosure<T> + ?Sized,

A more direct version of Closure::new which creates a Closure from a Box<dyn Fn>/Box<dyn FnMut>, which is how it’s kept internally.

Safety: Unwind safety is assumed when using this function, like using AssertUnwindSafe(...), this must be verified explicitly.

This version catches panics when unwinding is available.

Source

pub fn borrow<'a, F>(t: &'a F) -> ScopedClosure<'a, F::Static>
where F: IntoWasmClosureRef<'a, T> + MaybeUnwindSafe + ?Sized,

Creates a scoped closure by borrowing an immutable Fn closure with panic unwind support.

This is the recommended way to pass closures to JavaScript for immediate/ synchronous use. Unlike Closure::own, this does not require the closure to be 'static, allowing you to capture references to local variables.

Use borrow_mut when you need to mutate captured state.

The returned ScopedClosure<'a, _> has lifetime 'a from the closure reference, which means it cannot outlive the closure or any data the closure captures.

Supports unwind via its UnwindSafe bound when building with panic=unwind. The resulting closure can be upcasted to FnMut using upcast_ref.

§When to use scoped closures

Use ScopedClosure::borrow or ScopedClosure::borrow_mut when:

  • JavaScript will call the closure immediately and not retain it
  • You need to capture non-'static references
  • You want automatic cleanup when the ScopedClosure is dropped
§Closure lifetime

The JavaScript function is only valid while the ScopedClosure exists. Once dropped, the JavaScript function is invalidated. If JavaScript retains a reference and calls it later, it will throw: “closure invoked recursively or after being dropped”.

Rust’s borrow checker ensures ScopedClosure cannot outlive the closure’s captured data, preventing use-after-free bugs.

§Example
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn call_with_value(cb: &ScopedClosure<dyn Fn(u32)>, value: u32);
    fn call_fnmut(cb: &ScopedClosure<dyn FnMut(u32)>, value: u32);
}

let data = vec![1, 2, 3];
let f = |x| {
    println!("data len: {}, x: {}", data.len(), x);
};
let closure = ScopedClosure::borrow(&f);
call_with_value(&closure, 42);
// Can also upcast to FnMut
call_fnmut(closure.upcast_ref(), 42);
Source

pub fn borrow_assert_unwind_safe<'a, F>( t: &'a F, ) -> ScopedClosure<'a, F::Static>
where F: IntoWasmClosureRef<'a, T> + ?Sized,

Like borrow, but catches panics without requiring MaybeUnwindSafe.

Safety: Unwind safety is assumed when using this function, like using AssertUnwindSafe(...), this must be verified explicitly.

Source

pub fn borrow_aborting<'a, F>(t: &'a F) -> ScopedClosure<'a, F::Static>
where F: IntoWasmClosureRef<'a, T> + ?Sized,

Like borrow, but does not catch panics.

If the closure panics, the process will abort. This variant does not require UnwindSafe.

Note: Not unwind safe. Prefer borrow or borrow_assert_unwind_safe when possible.

Source

pub fn borrow_mut<'a, F>(t: &'a mut F) -> ScopedClosure<'a, F::Static>
where F: IntoWasmClosureRefMut<'a, T> + MaybeUnwindSafe + ?Sized,

Creates a scoped closure by mutably borrowing a FnMut closure.

Use this for closures that need to mutate captured state. For closures that don’t need mutation, prefer borrow which creates an immutable Fn closure that is more likely to satisfy UnwindSafe automatically.

See borrow for full documentation on scoped closures.

§Example
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn call_three_times(cb: &ScopedClosure<dyn FnMut(u32)>);
}

let mut sum = 0;
let closure = ScopedClosure::borrow_mut(&mut |x: u32| {
    sum += x;
});
call_three_times(&closure);
// closure dropped, `sum` is accessible again
assert_eq!(sum, 6); // 1 + 2 + 3
Source

pub fn borrow_mut_assert_unwind_safe<'a, F>( t: &'a mut F, ) -> ScopedClosure<'a, F::Static>
where F: IntoWasmClosureRefMut<'a, T> + ?Sized,

Like borrow_mut, but catches panics without requiring MaybeUnwindSafe.

Safety: Unwind safety is assumed when using this function, like using AssertUnwindSafe(...), this must be verified explicitly.

Source

pub fn borrow_mut_aborting<'a, F>(t: &'a mut F) -> ScopedClosure<'a, F::Static>
where F: IntoWasmClosureRefMut<'a, T> + ?Sized,

Like borrow_mut, but does not catch panics.

If the closure panics, the process will abort. This variant does not require UnwindSafe.

Note: Not unwind safe. Prefer borrow_mut or borrow_mut_assert_unwind_safe when possible.

Source§

impl<T> ScopedClosure<'static, T>
where T: ?Sized + WasmClosure,

Source

pub fn into_js_value(self) -> JsValue

Release memory management of this closure from Rust to the JS GC.

When a Closure is dropped it will release the Rust memory and invalidate the associated JS closure, but this isn’t always desired. Some callbacks are alive for the entire duration of the program or for a lifetime dynamically managed by the JS GC. This function can be used to drop this Closure while keeping the associated JS function still valid.

If the platform supports weak references, the Rust memory will be reclaimed when the JS closure is GC’d. If weak references is not supported, this can be dangerous if this function is called many times in an application because the memory leak will overwhelm the page quickly and crash the wasm.

§Safety Note

This method is only available on 'static closures. Calling it on a borrowed ScopedClosure would be unsound because the closure data would become invalid when the borrow ends.

Source

pub fn forget(self)

Same as mem::forget(self).

This can be used to fully relinquish closure ownership to the JS.

§Safety Note

This method is only available on 'static closures. Calling it on a borrowed ScopedClosure would be unsound because the closure data would become invalid when the borrow ends.

Source

pub fn once<F, A, R>(fn_once: F) -> Self
where F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,

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<dyn 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<dyn FnMut() -> String> = Closure::once(f);

Note: the A and R type parameters are here just for backward compat and will be removed in the future.

Source

pub fn once_wrap<F, A, R>(fn_once: F) -> Self
where F: WasmClosureFnOnceAbort<T, A, R>,

Create a Closure from a function that can only be called once.

Unlike once, this version does NOT catch panics and does NOT require UnwindSafe. If the closure panics, the process will abort.

Use this when:

  • Your closure captures types that aren’t UnwindSafe (like Rc<Cell<T>>)
  • You don’t need panic catching across the JS boundary
  • You prefer abort-on-panic behavior

Since we have no way of enforcing that JS cannot attempt to call this FnOnce(A...) -> R more than once, this produces a Closure<dyn FnMut(A...) -> R> that will dynamically throw a JavaScript error if called more than once.

Note: Not unwind safe. Prefer once or once with AssertUnwindSafe when possible.

Note: the A and R type parameters are here just for backward compat and will be removed in the future.

Source

pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
where F: WasmClosureFnOnce<T, A, R> + MaybeUnwindSafe,

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 wasm_bindgen::{prelude::*, JsCast};

let f = Closure::once_into_js(move || {
    // ...
});

assert!(f.is_instance_of::<js_sys::Function>());

Note: the A and R type parameters are here just for backward compat and will be removed in the future.

Source

pub fn once_into_js_wrap<F, A, R>(fn_once: F) -> JsValue
where F: WasmClosureFnOnceAbort<T, A, R>,

Convert a FnOnce(A...) -> R into a JavaScript Function object.

Unlike once_into_js, this version does NOT catch panics and does NOT require UnwindSafe. If the closure panics, the process will abort.

If the JavaScript function is invoked more than once, it will throw an exception.

Unlike Closure::once_aborting, 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.

Note: Not unwind safe. Prefer once_into_js or once_into_js with AssertUnwindSafe when possible.

Note: the A and R type parameters are here just for backward compat and will be removed in the future.

Trait Implementations§

Source§

impl<T: ?Sized> AsRef<JsValue> for ScopedClosure<'_, T>

Source§

fn as_ref(&self) -> &JsValue

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl<T> Debug for ScopedClosure<'_, T>
where T: ?Sized,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: ?Sized> Drop for ScopedClosure<'_, T>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl<T: ?Sized + WasmClosure> ErasableGeneric for ScopedClosure<'_, T>

Source§

type Repr = ScopedClosure<'static, dyn FnMut()>

The singular concrete type that all generic variants can be transmuted on
Source§

impl<T> IntoWasmAbi for &ScopedClosure<'_, T>
where T: WasmClosure + ?Sized,

Source§

type Abi = u32

The Wasm ABI type that this converts into when crossing the ABI boundary.
Source§

fn into_abi(self) -> u32

Convert self into Self::Abi so that it can be sent across the wasm ABI boundary.
Source§

impl<T> IntoWasmAbi for ScopedClosure<'static, T>
where T: WasmClosure + ?Sized,

'static closures can be passed by value to JS, transferring ownership.

This is useful for one-shot callbacks where you want JS to own the closure. The closure will be cleaned up by JS GC (if weak references are supported) or will leak (if weak references are not supported).

§Example

#[wasm_bindgen]
extern "C" {
    fn set_one_shot_callback(cb: Closure<dyn FnMut()>);
}

let cb = Closure::new(|| { /* ... */ });
set_one_shot_callback(cb);  // Ownership transferred to JS
// No need to store or forget the closure
Source§

type Abi = u32

The Wasm ABI type that this converts into when crossing the ABI boundary.
Source§

fn into_abi(self) -> u32

Convert self into Self::Abi so that it can be sent across the wasm ABI boundary.
Source§

impl<T> OptionIntoWasmAbi for &ScopedClosure<'_, T>
where T: WasmClosure + ?Sized,

Source§

fn none() -> Self::Abi

Returns an ABI instance indicating “none”, which JS will interpret as the None branch of this option. Read more
Source§

impl<T> OptionIntoWasmAbi for ScopedClosure<'static, T>
where T: WasmClosure + ?Sized,

Source§

fn none() -> Self::Abi

Returns an ABI instance indicating “none”, which JS will interpret as the None branch of this option. Read more
Source§

impl<T: ?Sized> Unpin for ScopedClosure<'_, T>

Source§

impl<'a, 'b, T1, T2> UpcastFrom<ScopedClosure<'a, T1>> for ScopedClosure<'b, T2>
where T1: ?Sized + WasmClosure, T2: ?Sized + WasmClosure + UpcastFrom<T1>,

Auto Trait Implementations§

§

impl<'a, T> Freeze for ScopedClosure<'a, T>
where T: ?Sized,

§

impl<'a, T> RefUnwindSafe for ScopedClosure<'a, T>
where T: RefUnwindSafe + ?Sized,

§

impl<'a, T> Send for ScopedClosure<'a, T>
where T: Send + ?Sized,

§

impl<'a, T> Sync for ScopedClosure<'a, T>
where T: Sync + ?Sized,

§

impl<'a, T> UnsafeUnpin for ScopedClosure<'a, T>
where T: ?Sized,

§

impl<'a, T> UnwindSafe for ScopedClosure<'a, T>
where T: UnwindSafe + ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ReturnWasmAbi for T
where T: IntoWasmAbi,

Source§

type Abi = <T as IntoWasmAbi>::Abi

Same as IntoWasmAbi::Abi
Source§

fn return_abi(self) -> <T as ReturnWasmAbi>::Abi

Same as IntoWasmAbi::into_abi, except that it may throw and never return in the case of Err.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = <Self as ErasableGeneric>::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = <Self as ErasableGeneric>::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more