我正在使用兩個不同的庫(特別是napi-rsand callback-future),并希望在另一個庫中呼叫FnOnce一個庫中的Fn函式。具體來說,我正在嘗試向 JavaScript 公開一個 Rust 函式,該函式Future在呼叫時完成 a 。
由于暴露的 JS 函式在技術上可以隨時被捕獲和呼叫,Rust 無法保證函式只會被呼叫一次,所以它不得不假設函式會被呼叫多次。但是,callback-future要求 aFuture只完成一次(通過呼叫 an FnOnce)。我無法修改這兩個簽名,它們對于各自的用例都是準確的。我怎樣才能讓兩者一起作業,以便我可以Future在Fn回呼中解決 a ?
FnOnce我知道多次呼叫是不行的,我可以使用unsafe代碼或在運行時強制執行該函式只呼叫一次。FnOnce在呼叫FnOnce. 目前我擁有的是:
// Create a `CallbackFuture` to wait for JS to respond.
// `complete` is an `FnOnce` to mark the `Future` as "Ready".
CallbackFuture::<Result<String, Error>>::new(move |complete| {
thread_safe_js_function.call(Ok(Args {
// Other arguments...
// Callback for JS to invoke when done.
// `Fn` because JS could theoretically call this multiple times,
// though that shouldn't be allowed anyways.
callback: Box::new(move |ctx| {
let result = ctx.get::<JsString>(0)?.into_utf8()?.as_str()?.to_owned();
// Complete the `Future` with the result.
complete(Ok(result));
ctx.env.get_undefined() // Return `undefined` to JS.
}),
}), ThreadsafeFunctionCallMode::Blocking);
}).await
這給了我錯誤:
error[E0507]: cannot move out of `complete`, a captured variable in an `Fn` closure
--> node/src/lib.rs:368:15
|
352 | CallbackFuture::<Result<PathBuf, Error<BundleErrorKind>>>::new(move |complete| {
| -------- captured outer variable
...
358 | callback: Box::new(move |ctx| {
| ________________________________-
... |
368 | | complete(Ok(result));
| | ^^^^^^^^ move occurs because `complete` has type `std::boxed::Box<dyn FnOnce(Result<PathBuf, Error>) Send>`, which does not implement the `Copy` trait
369 | |
370 | | ctx.env.get_undefined()
371 | | }),
| |_____________- captured by this `Fn` closure
For more information about this error, try `rustc --explain E0507`.
complete雖然錯誤抱怨在閉包之間移動,但我的理解是這是不允許的,因為FnOnce我試圖從Fn. 如果有另一種方法可以解決關閉問題,那么我想這也可能是一個可行的解決方案。
此外,如果 N-API 中有一種方法可以接受Promise結果而await不是通過回呼,那也可能是一個很好的選擇。我相信你可以await在PromiseRust 中使用,但是 AFAICT 無法從執行緒安全的 N-API 函式接收同步結果,因為它napi-rs似乎忽略了回傳值。
到目前為止,我發現的唯一解決方案是 fork callback-futureAPI 以complete從 an更改FnOnce為 an Fn,這顯然不是一個很好的解決方案。我還試圖std::mem::transmute()從認為只要我只呼叫一次函式就可以強制以這種方式進行強制FnOnce轉換。Fn但是,在呼叫時這樣做會出現段錯誤,所以我認為它不會按照我想要的方式作業。非常感謝這里的任何想法!
uj5u.com熱心網友回復:
由于您沒有我們可以自己編譯的示例并且缺少一些細節,因此我將解決您問題的核心:您如何呼叫 an FnOncefrom an Fn?
您已經知道的第一個問題:如果您嘗試FnOnce直接呼叫 ,這是不允許的,因為它會消耗值,這將使呼叫閉包本身FnOnce成為Fn.
第二個是如果你嘗試使用類似Option它的take()方法,你會發現它Fn不能改變它的捕獲狀態(它必須FnMut這樣做)。
解決方案是將 an 包裝Option在提供內部可變性的型別中。根據您是否還需要Fn,Send Sync您可以使用Cell或Mutex。
使用Cell,而不是 Send Sync,它看起來像這樣:
use std::cell::Cell;
fn print_owned(x: String) {
println!("print_owned called with {:?}", x);
}
fn call_twice(f: impl Fn()) {
f();
f();
}
fn main() {
let foo = "foo".to_string();
let fnonce = move || print_owned(foo);
let maybe_fnonce = Cell::new(Some(fnonce));
call_twice(move || {
match maybe_fnonce.take() {
Some(inner) => inner(),
None => println!("maybe_fnonce is None"),
}
});
}
當閉包傳遞給call_twice()呼叫take()時Cell,內部值被提取并替換為None。如果再次呼叫該函式,則內部值將是None先前放在那里的值。這也意味著您可以檢測到這種情況,并可能將問題發回 JavaScript 端。
如果需要關閉,Send Sync則可以改為使用Mutex<Option<_>>:
use std::sync::Mutex;
fn print_owned(x: String) {
println!("print_owned called with {:?}", x);
}
fn call_twice(f: impl Fn()) {
f();
f();
}
fn main() {
let foo = "foo".to_string();
let fnonce = move || print_owned(foo);
let maybe_fnonce = Mutex::new(Some(fnonce));
call_twice(move || {
match maybe_fnonce.lock().unwrap().take() {
Some(inner) => inner(),
None => println!("maybe_fnonce is None"),
}
});
}
您需要做的就是將此技術應用于您的具體情況。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/485593.html
標籤:javascript 锈 关闭 未来 接口
