8 个版本
0.2.0 | 2019年11月7日 |
---|---|
0.1.7 | 2019年5月15日 |
0.1.6 | 2019年4月21日 |
0.1.5 | 2019年3月2日 |
0.1.2 | 2019年1月27日 |
#514 in 异步
55KB
1K SLoC
Rust的轻量级主循环
因为Rust的本地GUI故事从主循环开始。
(尽管这个库也可能适用于许多其他用例。)
目标
- 支持基于回调和async/await的API
- 跨平台
- 桌面应用程序的性能开销可忽略不计
- 绑定到每个平台上的最佳后端
- 没有额外的后台线程
- 提供对原始句柄的访问以允许平台特定的扩展
非目标
- 不惜一切代价避免分配和虚拟调度
- I/O可伸缩性
- no_std功能
状态
该库具有运行代码的功能
- ASAP(主循环有机会运行时),
- 超时后,
- 定期间隔,
- ASAP,但在另一个线程中,
- 当I/O对象准备好读取或写入时。
它可以通过以下方式实现:
- 安排回调
- 安排0.3 future
- 安排async fn
成熟度:刚刚运行起来,未经战斗测试。它也是一个概念验证,以引发讨论和兴趣。也就是说,它正在等待您给它一个机会,尝试它,看看您喜欢什么,不喜欢什么,缺少哪些功能等!
不安全块:仅在后端/FFI级别。在引用(Rust std)后端中,根本不存在不安全代码。
Rust版本:最新稳定版应该没问题。
支持的平台
目前
- Win32 API - 使用
--features "win32"
编译 - Glib - 使用
--features "glib"
编译 - Rust std - 参考实现,不支持I/O。
愿望清单
- OS X / Cocoa
- Wasm / web(由于我们无法控制主循环,因此有限)
- QT
- iOS
- Android
示例
借用
如果您有权访问主循环,它支持借用闭包,因此您不需要克隆/refcell您的数据
// extern crate thin_main_loop as tml;
let mut x = false;
{
let mut ml = tml::MainLoop::new();
ml.call_asap(|| {
x = true; // x is mutably borrowed by the closure
tml::terminate();
});
ml.run();
}
assert_eq!(x, true);
非借用,以及一个定时器
如果您没有访问主循环的权限,您仍然可以安排 'static
回调
// extern crate thin_main_loop as tml;
let mut ml = tml::MainLoop::new();
ml.call_asap(|| {
// Inside a callback we can schedule another callback.
tml::call_after(Duration::new(1, 0), || {
tml::terminate();
});
})
ml.run();
// After one second, the main loop is terminated.
I/O
需要“glib”或“win32”功能。
以下示例连接到TCP服务器并打印所有传入的数据。
// extern crate thin_main_loop as tml;
let mut io = TcpStream::connect(/* ..select server here.. */)?;
io.set_nonblocking(true)?;
let wr = tml::IOReader { io: io, f: move |io: &mut TcpStream, x| {
// On incoming data, read it all
let mut s = String::new();
let r = io.read_to_string(&mut s);
// If we got something, print it
if s != "" { println!(s); }
// This is TcpStream's way of saying "connection closed"
if let Ok(0) = r { tml::terminate(); }
}
let mut ml = MainLoop::new()?;
ml.call_io(wr)?;
ml.run();
异步函数
以下代码等待一秒后终止程序。
// extern crate thin_main_loop as tml;
use std::time::{Instant, Duration};
async fn wait_until(n: Instant) {
delay(n).await.unwrap();
}
let mut x = tml::futures::Executor::new().unwrap();
let n = Instant::now() + Duration::from_millis(1000);
x.block_on(wait_until(n));
后台
回调
大多数本地GUI的API都是基于回调构建的。当按钮被点击时,你会得到一个回调。但问题是,回调实际上并不 rustic:如果你想在两个不同的回调中访问你的按钮对象,你最终不得不使用 Rc/RefCell。虽然这并不是世界末日,但人们一直在尝试其他更 rustic 的 API 设计。
但这将是另一个库的任务。如果我们首先创建一个薄薄的跨平台库,该库绑定到本地GUI API,我们就可以在此基础上尝试创建一个更 rustic 的 API。在我们能够创建窗口和按钮之前,我们需要创建一个主循环,它可以处理这些对象的事件。因此,这个库。
其他Rust主循环
Mio
Mio 也是一个跨平台的主循环,但Mio的设计目标与最终使其不适用于本地GUI应用程序的目标大不相同。Mio的主要用例是高度可扩展的服务器,因此它绑定到 IOCP/epoll 等,可以无问题处理数千个TCP套接字,但它与GUI库的本地API集成不佳:IOCP 线程无法处理 Windows 消息等。这个库绑定到 PeekMessage/GMainLoop 等,这使得它适用于具有本地外观和感觉的GUI应用程序。
此外,Mio在避免分配方面做得更好,但代价是牺牲了易用性。
Calloop
Calloop 是一个具有与这个crate非常相似的回调式API的事件循环。然而,它是建立在Mio之上的,因此它绑定到了不适用于本地GUI应用程序的本地API。
Winit
Winit 包含一个事件循环,该crate的目的是创建窗口。事件循环不是基于回调的,而是基于枚举(每个事件都是一个枚举,你需要自己进行分发)。Winit的重点更在于获取窗口和自定义绘图(通过OpenGL,Vulkan等),而不是绘制本地GUI小部件,但尽管如此,它仍与这个crate有一些共同点。
IUI / libui
IUI 是 Rust 对 libui 的绑定,libui 是用 C 编写的跨平台 GUI 库。其事件循环提供回调,就像这个库一样。相比之下,这个库是纯Rust,并直接绑定到本地库,跳过了一个抽象层,因此更容易构建。我也希望随着时间的推移,这个库能够提供更好的 Rust 集成以及更多的灵活性,使其不仅适用于纯GUI应用程序,即使当前的主要用例是如此。
依赖项
~0.6–3MB
~58K SLoC