Rust(with Iced)でGUIのタイマーを作ってみた
対局時計を作ろうと思い、その手始めとしてタイマーを作ってみたのですが非同期が絡む都合で案外大変だったので共有します。なお以下のことは基本的に、IcedのGitHubのexamples内にあるstopwatchを文章化したような内容になっています。
環境
- rustc: 1.46.0
- cargo: 1.46.0
- iced: 0.1.1
- futures-timer: 3.0.2
ライブラリ選定
今回はGUIライブラリとしてパッと見、使いやすそうだったのでIcedを選びました。IcedはElm風のクロスプラットフォームなGUIライブラリです。またIcedは非同期ライブラリをiced_futures
という形でラッピングしているため、非同期ライブラリをfutures、tokio、async-stdの中から好きに選べるのですが、今回はデフォルトのfuturesを選びました。
Icedで非同期
Icedでは非同期な処理をする際の基本の形は以下のような感じです。
pub struct App;
impl iced::Application for App {
fn subscription(&self) -> iced::Subscription<Self::Message> {
iced::Subscription::from_recipe(/* impl iced_futures::subscription::Recipe な何か */)
.map(/* 後述のTimerRecipe::OutputからApp::Messageへの変換 */)
}
/* その他newなど要求されるもの */
}
このRecipe
というのが非同期処理の部分で、適当なstruct
でimpl Recipe
をしてその中でRecipe::stream(self, input: BoxStream<Event>) -> BoxStream<Self::Output>
に非同期処理をfutures::stream::Stream
の形で実装します。
futuresでタイマー
tokioやasync-stdを使う場合、interval
のような関数があり、またiced_futures::time
が増える(futuresのみの場合は存在しない)ので苦労はないのですが、futuresには残念ながらありません。今回は単にタイマーを使いたかっただけなのでasync-stdやtokioなど大層なものを入れる気にならず、代わりにfutures-timerというそのものな名前のものがあったのでこれを使います。
futures-timerは非常にシンプルで、futures_timer::Delay
というimpl core::future::Future
なstructが有るだけです。今回はこれをBoxStream
にしなければいけず、またtokioのinterval
のように繰り返さなければいけないので、ここでfuturesのstream::unfold
を使います。具体的には以下のようになります。
pub struct TimerRecipe(std::time::Duration);
impl<H: std::hash::Hash, Event> iced_futures::subscription::Recipe<H, Event> for TimerRecipe {
fn stream(self: Box<Self>, _input: BoxStream<Event>) -> BoxStream<Self::Output> {
stream::unfold(self.0, |d| async move {
Delay::new(d).await;
let now = Instant::now();
Some((now, d))
})
.boxed()
}
/* その他hash等要求されるもの */
}
ここまで作れば後は(特にiced::Application::subscription
を明示的に呼ぶこと無く)普通に実行すればTimerRecipe
に指定したDuration
で定期的にApp::Message
が通知されApp::update
で受け取ることができます。
あとがき
今回は単にタイマーを作りたかっただけなのでfuturesで頑張って書きましたが、もう少しいろいろと非同期で行う場合には素直にtokioやasync-std(ただしasync-stdの場合はunstable featureを有効化する必要があります)を使うと良いかもしれないと感じました。Rustの非同期ライブラリは決定版というものが(恐らく)ないですが、tokioが最有力な感じでしょうか。
Discussion