Amb*_*jak 5 callback event-driven rust
我想知道如何在 Rust 中使用带有回调的可组合事件驱动设计。根据我现有的实验,我开始怀疑 Rust 中的所有权系统更适合自上而下的过程代码,并且在事件驱动设计中回调所需的父对象的引用方面存在问题。
本质上,我希望看到以下 C++ 代码的 Rust 等效项。该代码实现了一个使用繁忙循环调度 Timer 事件的 EventLoop、一个带有 timer_expired 回调的 Timer 类以及一个以 500 毫秒为间隔调度计时器的 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;
}
Run Code Online (Sandbox Code Playgroud)
该代码按照标准 C++14 工作,我相信是正确的。请注意,在认真的实现中,出于性能原因,我将使 running_timers 成为侵入性链接列表而不是 std::list 。
以下是该解决方案的一些属性,我需要在 Rust 等效项中看到它们:
我可以展示一些我尝试过的东西,但我认为它没有用。我遇到的主要痛点是通过回调特征涉及的所有父对象的引用来满足借用规则。
我怀疑 RefCell 或类似的东西可能是解决方案的一部分,可能是一个或多个具有内部不安全部分的特殊类,允许将东西粘合在一起。也许 Rust 通常提供的引用安全的某些部分只能在运行时通过恐慌来保证。
更新
我已经用 Rust 创建了一个原型实现,但它并不安全。具体来说:
| 归档时间: |
|
| 查看次数: |
4163 次 |
| 最近记录: |