我想知道如何在Rust中使用带有回调的可组合事件驱动设计。从我现有的实验中,我开始怀疑Rust中的所有权系统更适合自上而下的过程代码,并且在事件驱动设计中对回调所需的父对象的引用存在问题。
基本上,我希望看到以下C ++代码的Rust等价物。该代码实现了一个EventLoop,它使用busy循环调度Timer事件,Timer类具有timer_expired回调,以及一个以500ms为间隔调度计时器的User类。
#include <stdio.h>
#include <assert.h>
#include <list>
#include <chrono>
#include <algorithm>
using namespace std::chrono;
// Wrapping code in System class so we can implement all functions within classes declarations...
template <typename Dummy=void>
struct System {
class Timer;
class EventLoop {
friend class Timer;
private:
std::list<Timer *> m_running_timers;
bool m_iterating_timers;
typename std::list<Timer *>::iterator m_current_timer;
void unlink_timer (Timer *timer)
{
auto it = std::find(m_running_timers.begin(), m_running_timers.end(), timer);
assert(it != m_running_timers.end());
if (m_iterating_timers && it == m_current_timer) {
++m_current_timer;
}
m_running_timers.erase(it);
}
public:
EventLoop()
: m_iterating_timers(false)
{
}
milliseconds get_time()
{
return duration_cast<milliseconds>(system_clock::now().time_since_epoch());
}
void run()
{
while (true) {
milliseconds now = get_time();
m_iterating_timers = true;
m_current_timer = m_running_timers.begin();
while (m_current_timer != m_running_timers.end()) {
Timer *timer = *m_current_timer;
assert(timer->m_running);
if (now >= timer->m_expire_time) {
m_current_timer = m_running_timers.erase(m_current_timer);
timer->m_running = false;
timer->m_callback->timer_expired();
} else {
++m_current_timer;
}
}
m_iterating_timers = false;
}
}
};
struct TimerCallback {
virtual void timer_expired() = 0;
};
class Timer {
friend class EventLoop;
private:
EventLoop *m_loop;
TimerCallback *m_callback;
bool m_running;
milliseconds m_expire_time;
public:
Timer(EventLoop *loop, TimerCallback *callback)
: m_loop(loop), m_callback(callback), m_running(false)
{
}
~Timer()
{
if (m_running) {
m_loop->unlink_timer(this);
}
}
void start (milliseconds delay)
{
stop();
m_running = true;
m_expire_time = m_loop->get_time() + delay;
m_loop->m_running_timers.push_back(this);
}
void stop ()
{
if (m_running) {
m_loop->unlink_timer(this);
m_running = false;
}
}
};
class TimerUser : private TimerCallback {
private:
Timer m_timer;
public:
TimerUser(EventLoop *loop)
: m_timer(loop, this)
{
m_timer.start(milliseconds(500));
}
private:
void timer_expired() override
{
printf("Timer has expired!\n");
m_timer.start(milliseconds(500));
}
};
};
int main ()
{
System<>::EventLoop loop;
System<>::TimerUser user(&loop);
loop.run();
return 0;
}
代码作为标准C ++ 14工作,我相信是正确的。注意,在一个严肃的实现中,出于性能原因,我会使running_timers成为一个侵入式链表而不是std :: list。
以下是此解决方案的一些属性,我需要在Rust等效项中看到:
我可以展示一些我尝试的东西,但我认为它不会有用。我所遇到的主要痛点是满足借用规则以及对回调特征所涉及的父对象的所有引用。
我怀疑RefCell或类似的东西可能是解决方案的一部分,可能是一个或多个带有内部不安全部件的特殊类,它们可以将东西粘合在一起。也许Rust提供的参考安全的某些部分只能在运行时通过恐慌来保证。
更新
我在Rust中创建了prototype implementation这个,但它不安全。具体做法是: