Хранение замыкания в 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 если вы не хотите, чтобы какие-либо замыкания захватывали ссылки на их окружение.

Другие вопросы по тегам