[Rust + GUI] icedからDioxusに改宗した話

先日、icedというGUIライブラリについての記事を書きました ([icedに入門したのでメモを残す](https://qiita.com/Yosh31207/items/81632c6e2b54eb2f6f7c)) 。その後、Dioxusという別のライブラリに鞍替えしたので、考えたこと等をメモとして残します。Tauriやeguiについてもちょっとだけ触れます。

いずれもマルチプラットフォームなフレームワークで、Webも対象となる技術ですが、ここではデスクトップアプリを作ることを想定しています。

この内容は2024年6月時点の情報を元にしています。バージョンはicedが0.12.1、Dioxusが0.5.1です。

## TL;DR

* 筆者個人としてはicedの方が好み
  * Elmアーキテクチャはシンプルで分かりやすいし、Rustの良さとも合致する

* icedは機能面が弱い
  * IMEが動かないのが致命的。これさえなければicedを使いたいと思えるくらい

* 実用面ではDioxusの方が良さそう
  * GUI部分はWebViewを使うため、UIの機能不足の心配は少ない
  * icedに比べてドキュメントが充実している
  * CLIツールやVS Code用の拡張機能など、周辺ツールも整備されている

## icedについて

https://github.com/iced-rs/iced

Elmアーキテクチャを元にしたGUIフレームワークです。概要は[先日の記事](https://qiita.com/Yosh31207/items/81632c6e2b54eb2f6f7c)や他の方の記事を参照してください。ここでは他と比較して感じたことを書きます。

### Elmアーキテクチャ

なんといってもこれ。Dioxusに触れて改めて感じたのですが、icedのアーキテクチャはとてもシンプルで分かりやすいです。

DioxusとDioxusのアーキテクチャの比較は、フロントエンドにおけるReact vs Elmの比較とほぼ同じものになると思います。他の方の記事ですが、以下の記事で触れられている内容にすごく納得しました。

https://jinjor-labo.hatenablog.com/entry/2023/03/30/061358

引用:

> Simple vs Easy という話がどこまで共通認識になっているのかは分からないが、 React に比べて Elm は圧倒的に「シンプル」側に倒した設計になっている。個人的に考えるシンプルさのメリットは以下のようなものだ。
>
> コードを見れば何が起きているか分かる \
> 覚えなければいけない特別なルールが少ない \
> 予想外の挙動が起きにくい
>
> ...
>
> 考えてみれば古来から言われてきた Model と View の分離、 React も是としている「ビューは純粋な関数」を何よりも体現できているのが、この Elm Architecture であると思う。

icedも同じアーキテクチャを採用しており、とても分かりやすいです。viewを生成する`fn view(&self)`は不変参照なので (RefCellなどの抜け道を使わない限り) 状態を変更できない点や、メッセージを`match`式できれいに扱える点など、Rustの構文ともよく合っています。メッセージの定義がやや冗長にも見えますが、それはシンプルさの裏返しであり、不具合を生みにくい設計に繋がると思います。

Elmが一定の人気を持つ理由が分かるような気がしました。

### ドキュメント

現時点のicedはドキュメントが充実しているとは言い難い状況です。TauriやDioxusはそれなりのドキュメントが用意されているので、それと比べると尚更です。

幸いにもexampleが充実しているので、それを参考にすれば大体はどうにかなると思います。

### UI

私自身はUIに拘りたいわけではないので、デフォルトのスタイルでも十分に感じました。icedでもスタイルの変更やUIの調整はできますが、HTML + CSSを使うフレームワークに比べればカスタマイズ性は低いかと思います。

### IME

現在のiced (ver.0.12.1) はIMEをサポートしていません。日本語の表示だけであれば可能ですが、日本語 (あるいは中国語や韓国語) の入力を必要とする場合はiced以外のものを選んだ方が良いと思います。

## Dioxusについて

https://github.com/DioxusLabs/dioxus

Reactに影響を受けたGUIフレームワークです。WebViewを使いますが、Tauriと違いRustのみで記述します。

Reactと同様、コンポーネントを中心にGUIを組み立てていきます。UIはJSXに似た`rsx!`マクロで記述します。

```Rust
#[component]
fn App() -> Element {
    let mut count = use_signal(|| 0);

    rsx! {
        h1 { "High-Five counter: {count}" }
        button { onclick: move |_| count += 1, "Up high!" }
        button { onclick: move |_| count -= 1, "Down low!" }
    }
}
```

フレームワーク自体の説明は公式のドキュメントが充実しているので、そちらを参照してください。

IMEも問題なく使えます。

### icedとの比較

#### アーキテクチャ

先にも書いたとおり、icedはElmアーキテクチャを元にしています。これはアプリの状態を管理する中央的なオブジェクトを用意し、ビューはそれを元に導出されるという考えです。

一方、Dioxusはコンポーネント指向のフレームワークです。コンポーネントという小さい単位の部品を作り、それを組み合わせることでアプリを作ります。ビューが状態を持たないicedとは逆に、Dioxusはコンポーネントが状態を持ちます。

Dioxusではコンポーネントに状態を持たせたり、非同期のタスクを動かしたりするためにフック (Hook) という仕組みを使いますが、使い方を誤ると無限ループなどの問題を引き起こします。icedは制御の流れが一方向であり、使い方を誤りにくい構造なので、この種の問題を起こしにくいです。Rustの強みである「問題をコンパイル時に防ぐ」をより享受できるのはicedの方かと思います。

#### UI

DioxusはシステムのWebViewを使うため、ボタン等のUIはブラウザに表示されるものとほぼ同じになります。

Web系の技術は十分に安定しているため、GUIアプリを作る上で安心感があります。先に述べた通り、icedにはIMEが動きませんが、Dioxusでは問題なく動きます。icedではボタンやテキストボックスなどのウィジェットをそれぞれicedが提供していますが、裏を返せば、icedが提供していないものは自作しない限り使えないことを意味します。Dioxusではブラウザがサポートするものは基本的に使えるため、「○○が無い」といったことは少ないかと思います。

### Tauriとの比較

TauriはバックエンドをRust、フロントエンドをJavaScript or TypeScriptで記述して、それをIPC (プロセス間通信) でやりとりするフレームワークです。一方DioxusはIPCを使わず、UIも含めてRustで記述します。

Rustだけで完結する分、TauriよりもDioxusの方が手軽だと個人的には感じました。Tauriだと

* フロントエンドとバックエンドでやりとりするためのAPIを決める
* フロント側をJavaScript/TypeScriptで記述する
* バックエンド側をRustで記述する

必要がありますが、DioxusはRustだけで完結するので、言語間を行き来したり、それぞれのフレームワークに頭を切り替えるためのスイッチングコストがかからない点がメリットです。

Tauriはフロント側を任意のフレームワークで書ける点が強みかもしれません。開発者がJavaScript/TypeScriptに慣れている、好みのフレームワークがある、既存の資産を使いたいといった場面であれば、こちらの方がマッチするかと思います。

将来の互換性を気にする場合はTauriの方が安心できるかと思います。Dioxusはまだバージョン1未満であり、今後もAPIが大幅に変更される可能性があります。実際、今年の4月のバージョンアップで大きな変更が行われました。Tauriは現時点でバージョン1.6.8と2.0.0-betaが公開されており、同じメジャー番号の間では今後も基本的な互換性が保証されると思います。Dioxusを使う場合は、フレームワークの成熟に応じて変更に追従するというマインドが必要になりそうです。

### ドキュメントについて

公式の[ガイド](https://dioxuslabs.com/learn/0.5/guide)が充実しているので、それを見るのが良いです。

※ URLから分かる通り、これはバージョン0.5用のガイドなので、新しいバージョンがある場合はそちらを参照してください。

### バージョンについて

Dioxusは今年3月にバージョンが0.5.0になり、前バージョンである0.4.3からAPIの破壊的変更を含む大きな変更が行われました。

ネットで情報を探す場合、見つけたコードは現在のDioxusと互換がない可能性があるので、その点に注意する必要があります。

公式ガイドの[Migration](https://dioxuslabs.com/learn/0.5/migration)の項目にDioxus 0.4から0.5への移行ガイドがあるので、古い情報を参照する際はこれと突き合わせて見るのが良さそうです。

分かりやすい点でいえば、0.5以降は関数の引数に``cx: Scope``をとらなくなったので、それで判別できるかと思います。

```Rust
// 0.4まで
fn App(cx: Scope) -> Element {
    cx.render(rsx! {
        ...
    })
}

// 0.5以降
fn App() -> Element {
    rsx! {
        ...
    }
}
```

### UI

TailwindというCSSフレームワークを使えるので、見た目をカスタマイズしたい場合はこれを使います。

### ツールについて

Dioxus CLIというツールがあり、開発の際はこれを使います。

```shell
cargo install dioxus-cli
```

`dx new`でプロジェクトを作成すると、目的 (例: Web, Desktop, TUI) に応じたテンプレートが作られるので、それを元に開発します。開発用サーバーの立ち上げやビルドなども`dx`コマンドを使います。

また、Dioxus用のVS Codeの拡張機能があります、これを入れると`rsx!`マクロの構文解析やフォーマットをしてくれるので、導入をおすすめします。

こういう周辺ツールも含め、Dioxusは勢いがあるなと感じました。

## 他の選択肢: egui

即時モード (immediate mode) で作るつもりが無かったので候補から外しましたが、eguiも選択肢の一つです。即時モード特有の制約はありますが、icedやDioxusよりも簡単なので、作るもの次第ではアリな選択肢かと思います。

https://github.com/emilk/egui

Webで動く[デモ](https://www.egui.rs/#demo)があり、どのような機能を使えるかが簡単に分かるようになっています。グラフやペイントツール、OpenGLを使った3D描画、HTTPリクエストなど一通りのサンプルがデモとして揃っているので、これらを使いたい人も安心して使えそうです。

GitHubのREADMEにある通り、Goalsとして The easiest to use GUI library (最も簡単なGUIライブラリ) を掲げる一方、Become the most powerful GUI library (最も強力なGUIライブラリ) はNon-Goalsとしています。目指す方向性が示されていて好感が持てます。

デモサイトでは日本語が表示されませんが、これは単に日本語フォントが読み込まれていないだけであり、必要な設定をすれば表示されます。IMEも、少なくとも自分の環境下では問題ありません。

## おわりに

IMEがなくて断念しましたが、icedは良いぞという気持ちもまだあります。IMEの問題もそのうち解決するかもしれないので、今後も動向を見守りたいところです。

Dioxusはまだ触れたばかりですが、こちらも魅力のあるフレームワークだと感じました。開発の勢いもあるので、今後が楽しみです。