Как создать функцию, возвращающую заемное значение?
У меня есть следующая функция как часть приложения Rust WASM для преобразования Box
закрытие в Rust-представлении для функции JavaScript.
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()
}
Однако компилятор жалуется, что возвращаемое значение использует заимствованное значение (полученное с callback.borrow()
) поэтому не могут быть возвращены.
Поэтому я решил добавить аннотации времени жизни, чтобы сообщить компилятору, что эта новая ссылка должна жить столько же, сколько и входные данные.
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()
}
К сожалению, это не помогло, и я получаю ту же ошибку. Что я здесь не так делаю?
2 ответа
Да, это не сработает.
callback.borrow().as_ref().unwrap().as_ref().unchecked_ref()
Давайте разберем это по шагам:
- Вы одалживаете
&RefCell<Option<Closure<FnMut()>>>
- так что теперь у вас естьRef<Option<...>>
, который является шагом № 1 ваших проблем. Когда это происходит, это промежуточное значение теперь имеет другое время жизни, чем'a
(уступает, если быть точным). Все, что вытекает из этого, унаследует это меньшее время жизни. Назови это'b
теперь - Ты тогда
as_ref
этотRef
превращая его вOption<&'b Closure<FnMut()>>
- Руст тогда преобразует
&'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
определяется, содержащий 'a
ссылка на Rc<RefCell<T>>
(где T
универсальный в вашем случае будет в конечном итоге Option<Closure<FnMut()>>
) и Ref
в T
с жизнью 'a
, автоматически заполняется на from_callback
конструктор.
В тот момент, когда вы создадите этот объект, у вас будет Ref
с тем же временем жизни, что и ссылка, которую вы указали в качестве аргумента, и проблема полностью исчезнет. Оттуда вы можете позвонить to_function
чтобы получить &'a
ссылка на ваш внутренний тип.
В этом есть одна проблема: пока существует один из этих объектов, вы (очевидно) не сможете borrow_mut()
на RefCell
, который может или не может убить ваш вариант использования (так как никто не использует RefCell
в шутку). Тем не менее, эти объекты относительно дешевы для создания экземпляров, поэтому вы можете позволить себе скопировать их, как только закончите с ними.
Пример с Function
а также Closure
типы заменены на u8
(так как js_sys
не может быть импортирован в песочницу) доступен здесь.
Хотя мне действительно нравятся ответы и объяснения Себастьяна, в итоге я пошел к предложению Омера использовать макрос просто для краткости. Я опубликую макрос на случай, если он кому-нибудь пригодится.
macro_rules! callback_to_function {
($callback:expr) => {
$callback
.borrow()
.as_ref()
.unwrap()
.as_ref()
.unchecked_ref()
};
}
Я оставлю ответ Себастьяна как принятый, так как считаю, что это более "правильный" способ решения этой проблемы, и он дает отличное объяснение.