🪵

Tauri 2.0で使えるRust UIテンプレートの比較

2024/12/09に公開

この記事はRust Advent Calendar 2024シリーズ2の9日目の記事です。

Rustでデスクトップ/モバイルアプリを製作するソリューションの一つとしてTauriがあります。
10月には、2.0がリリースされました🎉

そんなTauriでUIを実装する際には、選択肢として、次の3つがあります。

  • Rust
  • TypeScript / JavaScript
  • .NET

せっかくなので、UIもRustを使いたいですよね。となると、現状では4つのテンプレートから選択することになります。

  • Vanilla
  • Yew
  • Leptos
  • Sycamore

Vanillaは「自分でなんとかします」という意味なので、そうでなければ、他3つのいずれかを使うことになります。

しかし、この3つの違いがよく分からなかったため、少し調べてみました。

データで比較

まずはGitHubから分かるデータの比較です。

名称 最新版 最初のコミット スター数(GitHub) コントリビューター数
Yew (ユー) 0.21.0 (2023/9/29) 2017/12/16 30,000+ 400+
Leptos (レプトス) 0.7.0 (2024/12/1) 2022/8/1 16,000+ 300+
Sycamore (シカモア) 0.9.1 (2024/11/18) 2021/3/6 2,000+ 50+

Yewは最も歴史が古く、スター数も多いですが、近年は更新が少なめです。
Leptosは新しいながらもすでに16,000以上のスターを獲得しており、最も勢いがあります。
Sycamoreも精力的に更新が続けられています。

各フレームワークのサンプルを見てみましょう。

Yewのサンプル

Build a sample appにあったカウンターの実装を見てみます。

use yew::prelude::*;

#[function_component]
fn App() -> Html {
    let counter = use_state(|| 0);
    let onclick = {
        let counter = counter.clone();
        move |_| {
            let value = *counter + 1;
            counter.set(value);
        }
    };

    html! {
        <div>
            <button {onclick}>{ "+1" }</button>
            <p>{ *counter }</p>
        </div>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}

関数に対し functional_component マクロを適用することでコンポーネントを生成するようです。
コンポーネントの状態は use_state 関数で、ボタンを押したときの処理もクロージャーを使って書けます。

UIは html! マクロの中でJSXに似た構文で書けば良さそうです。

なお、構造体コンポーネントとしての実装もできるようです。

https://yew.rs/docs/next/advanced-topics/struct-components/introduction

Leptosのサンプル

A Basic Componentにあったカウンターの実装を見てみます。

use leptos::*;

#[component]
fn App() -> impl IntoView {
    let (count, set_count) = create_signal(0);

    view! {
        <button
            on:click=move |_| {
                // on stable, this is set_count.set(3);
                set_count(3);
            }
        >
            "Click me: "
            // on stable, this is move || count.get();
            {move || count()}
        </button>
    }
}

fn main() {
    leptos::mount_to_body(|| view! { <App/> })
}

こちらも、 component マクロを適用することでコンポーネントを生成するようです。
コンポーネントの状態は create_signal 関数で、ボタンを押したときの処理もクロージャーを使って書けます。

UIは view! マクロの中でJSXに似た構文で書けば良さそうです。

Builderパターンを使ったUIの構築もできるようです。

https://book.leptos.dev/view/builder.html

Sycamoreのサンプル

examplesにあったカウンターの実装を見てみます。

use sycamore::prelude::*;

#[component]
fn App() -> View {
    let mut state = create_signal(0i32);
    let increment = move |_| state += 1;
    let decrement = move |_| state -= 1;
    let reset = move |_| state.set(0);
    view! {
        div {
            p { "Value: " (state) }
            button(on:click=increment) { "+" }
            button(on:click=decrement) { "-" }
            button(on:click=reset) { "Reset" }
        }
    }
}

fn main() {
    sycamore::render(App);
}

こちらも、 component マクロを適用することでコンポーネントを生成するようです。
コンポーネントの状態は create_signal 関数で、ボタンを押したときの処理もクロージャーを使って書けます。

UIは view! マクロの中で、独自のDSLを使って書くようです。

Builderパターンを使ったUIの構築もできるようです。

https://sycamore.dev/book/guide/view-builder

結局のところ、何を使えばいいのか

3つのテンプレートを見てみましたが、単純な開発体験だけであれば、ほとんど差はなさそうに見えます。

なので、現状ではフィーリングで決めてしまっても問題ないと思います。

とはいえ、全く同じというわけでもないため、簡単に使い分けを考えてみたいと思います。

仮想DOM vs. きめ細かな反応性

インスパイアされたフレームワークの違いから、データ変化が発生した際のUIの更新ポリシーが異なります。

  • 仮想DOM (Virtual DOM)
    • データ変化を仮想DOM(メモリ上にコピーしたオブジェクト)に適用し、実DOMに差分を適用する
  • きめ細かな反応性 (Fine-Grained Reactivity)
    • 「データが変わったときに実DOMのどこを変更すべきか」を管理し、データの変更に対して直接DOMを書き換える

仮想DOMの方は、実DOMとの差分検出に一定処理時間がかかるため、きめ細かな反応性の方が表示速度上では有利です。

それぞれの思想は下記のとおりです。

名称 インスパイアされたフレームワーク ビュー更新設計
Yew React 仮想DOM
Leptos SolidJS きめ細かな反応性
Sycamore SolidJS きめ細かな反応性

The Elm Architecture

アーキテクチャ設計として、Elm言語で用いられているThe Elm Architecture (TEA)というものが存在します。

https://qiita.com/kazurego7/items/4f074c3e3972cf43dbcd

YewはElmにもインスパイアされており、構造体コンポーネントではTEAを使用することができます。

JSX vs. 独自DSL

YewとLeptosは、基本的には <> を使ったJSXに似た構文で書けます。

一方、Sycamoreは (){} を使った独自DSLを使って書きます。
今回は取り上げませんでしたが、Dioxusというフルスタックフレームワークもこれとよく似たDSLを採用しています。

まとめ

どれを使っても良いですが、迷ったら下記のように考えてみると良いかもしれません。

  • ReactかElmのように書きたい、または安定しているものを使いたい
    • Yew
  • メンテナンスが活発なものを使いたい、または表示速度にこだわりたい
    • Solid JSのように書きたい、またはそこそこ大きなコミュニティのあるものを使いたい
      • Leptos
    • div (class="container") { "Hello, world!" } という書き方が直感的に感じられた
      • Sycamore

Discussion