Хранение замыкания в HashMap
Чтобы выучить язык Rust, я беру старую библиотеку C++, в которой лежал, и пытаюсь преобразовать ее в Rust. Он использовал много замыканий в C++11, и у меня возникли трудности с переводом концепций.
В C++ у меня было что-то вроде этого:
// library.h
struct Event {
// just some data
};
class Object {
public:
// ...
std::function<void(Event&)>& makeFunc(std::string& s) {
return m_funcs[s];
}
// ...
private:
// ...
std::map<std::string, std::function<void(Event&)>> m_funcs;
// ...
};
// main.cpp using the library
int main()
{
Object foo;
foo.makeFunc("func1") = [&]{
// do stuff
};
return 0;
}
Часть, с которой у меня возникают проблемы, - это правильное хранение функций в коллекции Rust HashMap. Я попробовал это:
struct Event;
struct Object {
m_funcs : HashMap<String, FnMut(&Event)>
}
impl Object {
// send f as another parameter rather than try and return borrow
// compiler was complaining
fn makeFunc(&mut self, s : &str,f: FnMut(&Event)) {
self.m_funcs.insert(String::from_str(s), f);
}
}
но это говорит the trait core::marker::Sized is not implemented for the type 'for('r) core::ops::FnMut(&'r CreateEvent)'
Это имеет смысл, потому что FnMut
является особенностью, и поэтому не имеет известного размера для создания HashMap во время компиляции. Поэтому я считаю, что для хэш-карты потребуется фактический указатель, а не абстрактный тип. Так что я изменил это на это
struct Object {
m_funcs : HashMap<String, Box<FnMut(&Event)>>
}
impl Object {
fn makeFunc(&mut self, s : &str, f: &FnMut(&Event)) {
self.m_funcs.insert(String::from_str(s), Box::new(f));
}
}
теперь это говорит the trait 'for('r) core::ops::Fn<(&'r CreateEvent,)>' is not implemented for the type '&for('r) core::ops::FnMut(&'r CreateEvent)' [E0277]
на вставке. Эта ошибка не имеет никакого смысла для меня вообще. Может кто-нибудь объяснить мне, как правильно хранить ссылку на не экранирующее замыкание в HashMap?
1 ответ
Вы взяли &FnMut(&Event)
- черта объекта - и, после того, как его упаковать, хотите сохранить его как Box<FnMut(&Event)>
, Таким образом, вы требуете, чтобы &FnMut(&Event)
должен реализовать FnMut(&Event)
что он не (и явно не может, для FnMut.call_mut
принимает &mut self
).
Вы хотели взять произвольный тип, который реализует FnMut(&Event)
То есть использовать дженерики и принимать их по значению. Таким образом, подпись такова:
fn make_func<F: FnMut(&Event)>(&mut self, s: &str, f: F)
Это становится немного сложнее, чем это из-за продолжительности жизни, но то, что вы хотите сделать по этому поводу, может отличаться; Хранение распакованного закрытия со ссылочным аргументом в HashMap содержит больше информации по этой теме. Вот что я думаю, что вы, скорее всего, захотите:
struct Object<'a> {
m_funcs: HashMap<String, Box<FnMut(&Event) + 'a>>,
}
impl<'a> Object<'a> {
fn make_func<F: FnMut(&Event) + 'a>(&mut self, s: &str, f: F) {
self.m_funcs.insert(String::from_str(s), Box::new(f));
}
}
Вы можете удалить все 'a
в пользу только одного + 'static
связано на F
если вы не хотите, чтобы какие-либо замыкания захватывали ссылки на их окружение.