如何创建返回借用值的函数?

How to create a function returning a borrowed value?

我将以下函数作为 Rust WASM 应用程序的一部分,将 Boxed 闭包转换为 JavaScript 函数的 Rust 表示。

use js_sys::Function;

type Callback = Rc<RefCell<Option<Closure<FnMut()>>>>;

fn to_function(callback: &Callback) -> &Function {
    callback.borrow().as_ref().unwrap().as_ref().unchecked_ref()
}

但是,编译器抱怨 return 值使用了借用的值(通过 callback.borrow() 获得),因此无法 returned。

因此,我决定添加生命周期注释来通知编译器这个新引用应该与输入一样长。

use js_sys::Function;

type Callback = Rc<RefCell<Option<Closure<FnMut()>>>>;

fn to_function<'a>(callback: &'a Callback) -> &'a Function {
    callback.borrow().as_ref().unwrap().as_ref().unchecked_ref()
}

不幸的是,这没有帮助,我得到了同样的错误。我在这里做错了什么?

是的,这行不通。

callback.borrow().as_ref().unwrap().as_ref().unchecked_ref()

让我们逐步分解:

  1. 您正在借用 &RefCell<Option<Closure<FnMut()>>> - 所以您现在 Ref<Option<...>>,这是您问题的第 1 步。当这种情况发生时,这个中间值现在有一个不同于 'a 的生命周期(更准确地说,更差)。由此产生的任何事物都将继承这个较短的生命。现在叫它 'b
  2. 然后你as_ref这个Ref,把它变成Option<&'b Closure<FnMut()>>
  3. Rust 然后将 &'b Closure<FnMut()> 转换为 &'b Function

第 1 步出现了问题。由于一生的冲突,你留下了这个烂摊子。一个半体面的方法来解决它以下结构:

use std::rc::{Rc};
use std::cell::{RefCell, Ref};
use std::ops::Deref;

struct CC<'a, T> {
    inner: &'a Rc<RefCell<T>>,
    borrow: Ref<'a, T>
}

impl<'a, T> CC<'a, T> {
    pub fn from_callback(item:&'a Rc<RefCell<T>>) -> CC<'a, T> {
        CC {
            inner: item,
            borrow: item.borrow()
        }
    }
    pub fn to_function(&'a self) -> &'a T {
        self.borrow.deref()
    }
}

它有点笨拙,但这可能是最简洁的方法。

定义了一个新的 struct CC,其中包含对 Rc<RefCell<T>>'a 引用(在您的案例中,T 泛型最终将成为Option<Closure<FnMut()>>) 和生命周期为 'aRefT,在 from_callback 构造函数中自动填充。

在您生成此对象的那一刻,您将拥有一个 Ref,其生命周期与您作为参数提供的 ref 相同,从而使整个问题消失。从那里,您可以调用 to_function 来检索对您的内部类型的 &'a 引用。

这里有一个问题:只要存在 这些对象中的一个 ,您将(显然)无法 borrow_mut() RefCell,这可能会或可能不会杀死您的用例(因为人们不会为了它的乐趣而使用 RefCell)。尽管如此,这些对象的实例化成本相对较低,因此您可以在使用完它们后将它们装箱。

FunctionClosure 类型替换为 u8 的示例(因为 js_sys 无法导入沙箱)可用 here

虽然我真的很喜欢 Sébastien 的回答和解释,但为了简洁起见,我最终接受了 Ömer 使用宏的建议。我会 post 宏,以防其他人使用它。

macro_rules! callback_to_function {
  ($callback:expr) => {
    $callback
      .borrow()
      .as_ref()
      .unwrap()
      .as_ref()
      .unchecked_ref()
  };
}

我将 Sébastien 的回答作为已接受的答案,因为我相信这是解决此问题的 "correct" 方法,而且他提供了很好的解释。