將 Rust 閉包傳遞給導入的 JavaScript 函式
#[wasm_bindgen]
屬性支援以兩種變體將 Rust 閉包傳遞給 JavaScript
-
堆疊生命週期閉包,在傳遞閉包的導入 JavaScript 函式返回後,不應再次由 JavaScript 呼叫。
-
堆積分配的閉包,可以被呼叫任意次數,但在完成時必須明確地釋放。
堆疊生命週期閉包
具有堆疊生命週期的閉包會以 &dyn Fn
或 &mut dyn FnMut
特徵物件的形式傳遞給 JavaScript
# #![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; }); #}
一旦這些導入的函式返回,傳遞給它們的閉包將失效,任何未來從 JavaScript 呼叫這些閉包的嘗試都會引發例外。
閉包也支援像導出一樣的引數和傳回值,例如
# #![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) }); #}
堆積分配的閉包
有時,不希望使用堆疊生命週期閉包的約束。例如,您想要排程一個閉包,通過 setTimeout
在 JavaScript 中事件迴圈的下一個迴合執行。對於這種情況,您希望導入的函式返回,但 JavaScript 閉包仍然需要有效!
對於這種情況,您需要 Closure
類型,它在 wasm_bindgen
crate 中定義,在 wasm_bindgen::prelude
中導出,並表示「長壽命」閉包。
JavaScript 閉包的有效性與 Rust 中 Closure
的生命週期相關聯。一旦 Closure
被丟棄,它將釋放其內部記憶體並使相應的 JavaScript 函式失效,以便任何進一步呼叫它的嘗試都會引發例外。
像堆疊閉包一樣,Closure
支援 Fn
和 FnMut
閉包,以及引數和傳回值。
# #![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")) } #}