Rust の WASM フレームワーク "Yew" に入門しおみくじアプリを作成する
この記事では、Rustでウェブアプリケーションを構築するためのフレームワーク、Yewのセットアップと使い始め方について調査した内容を記載します。
Yew とは?
Yewは、Rustで書かれたモダンなWebアプリケーションフレームワークです。フロントエンドとバックエンドの両方をRustで開発できる、いわゆるフルスタックのフレームワークです。
フロントエンド機能
- React、Vue、Angularなどと同様のコンポーネントベースのアーキテクチャを採用
- Rustの特性を活かした以下のメリットがあります
- 型安全性: コンパイル時にエラーを検出でき、実行時エラーを防げます
- 高パフォーマンス: Rustの高速性を生かせます
- メモリ安全性: データ競合などのメモリ安全性問題が起きにくくなります
フルスタック開発のメリット
- 一つの言語(Rust)で開発できるため、学習コストを削減できます
- コード全体で一貫した構文やツールを使えるので、開発効率が向上します
- フロントエンドとバックエンドで同じデータ構造を使え、データの受け渡しが簡単になります
つまり、Yewを使えばRustの安全性、並行性、高速性をフルスタックで活かしたモダンなWebアプリケーション開発が可能になります。
Rustがフロントエンドで動作する理由
Rustがフロントエンドで動作できるのは、WebAssembly(Wasm) という技術のおかげです。
WebAssemblyとは、ウェブブラウザで高速に実行できるバイナリフォーマットです。JavaScriptと並行して動作し、ネイティブアプリケーションに近いパフォーマンスを発揮できます。
Rustは、その設計思想とパフォーマンスの高さからWebAssemblyの開発言語に適しているとされます。Rustで書かれたコードは、WebAssemblyにコンパイルすることで、ウェブブラウザ上で直接実行できます。
つまり、WebAssemblyを介してRustをフロントエンドで活用できるため、高速で安全なウェブアプリケーション開発が可能になります。Rustの強みを最大限に生かしつつ、JavaScriptとも親和性が高いのが特徴です。
「おみくじ」アプリを作成する
Yewフレームワークを使って「おみくじ」アプリを作成します。
ボタンをクリックすると、ランダムにおみくじの結果が選ばれ、画面に表示されるアプリです。
1. 前提条件と環境のセットアップ
まずは、Rustの開発環境をセットアップします。
Windowsの場合は、Microsoft公式ドキュメントを参考にしてください。
次に、WebAssemblyバイナリをビルドするための設定を行います。
rustup target add wasm32-unknown-unknown
-
rustupはRustツールチェーンのインストーラーで、ツールチェーンの管理と更新に使用します -
target addで新しいターゲットプラットフォームをコンパイルに追加します -
wasm32-unknown-unknownはWebAssembly(Wasm)出力用のターゲットで、Rustをブラウザで実行できるようになります
最後に、WebAssemblyアプリケーションのビルドとデプロイを可能にするTrunkパッケージをインストールします。
cargo install --locked trunk
-
cargo installでRustのパッケージマネージャ(Cargo)を使ってパッケージをインストールします -
--lockedフラグで、Cargo.lockファイルに記録された依存関係のバージョンを使用するよう指示し、ビルドの再現性を高めます -
trunkはWebAssemblyアプリのビルド・デプロイツールです
以上で準備が整いましたので、おみくじアプリの作成に取り掛ります。
2. 新しい Yew プロジェクトの作成
次に、新しいYewプロジェクトを作成します。以下のコマンドで新規プロジェクトを作成してください。
# --libフラグを指定してライブラリ(lib.rs)を作成します
cargo new --lib yew_fortune_app
cd yew_fortune_app
-
cargo newでCargoプロジェクトを新規作成します -
--libフラグを渡すことで、バイナリではなくライブラリを作成するよう指示しています -
yew_fortune_appは新しいプロジェクトの名前です
Yewアプリはライブラリとして実装されるため、--libフラグを指定してプロジェクトを作成しています。
作成されたプロジェクトにはsrc/lib.rsファイルがあり、ここにアプリケーションのコードを記述していきます。
3. 依存関係を追加する
新しいプロジェクトが作成されたら、Cargo.tomlファイルに必要な依存関係を追加します。
[package]
name = "yew_fortune_app"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
yew = "0.17"
wasm-bindgen = "0.2"
rand = "0.8"
# WebベースのWASMアプリでは、getrandomの機能を直接有効化する必要があります
getrandom = { version = "0.2", features = ["js"] }
-
[lib]セクションでcrate-typeをcdylibとrlibに設定しています。-
cdylib: WebAssemblyコンパクトデシリアライズドバイナリを出力します。 -
rlib: Rustライブラリを出力します。
-
-
[dependencies]セクションで、アプリに必要な依存関係を記載しています。-
yew: Yewフレームワーク本体です。 -
wasm-bindgen: Rust/WebAssembly間のデータバインディングを行います。 -
rand: 乱数生成ライブラリです。おみくじ結果をランダムに選ぶため使用します。 -
getrandom: 安全な乱数生成のための依存ライブラリで、WebAssemblyターゲット向けに"js"フィーチャを有効化しています。
-
4. シンプルな「おみくじ」アプリケーションを書く
次に、シンプルな「おみくじ」アプリケーションのコードを書きます。
src/lib.rsファイルを以下のように編集します。
// Rustの標準ライブラリからrand(乱数生成)をインポート
// Web上で動作するためのwasm_bindgenライブラリから、preludeモジュールをインポート
// Yewから、preludeモジュールをインポート
use rand::Rng;
use wasm_bindgen::prelude::*;
use yew::prelude::*;
// Modelという構造体を定義
// Reactでいうところのコンポーネントに相当する
// ComponentLinkはコンポーネント内のイベントハンドリングに使用
// fortuneはおみくじの結果を保持する文字列
struct Model {
link: ComponentLink<Self>,
fortune: String,
}
// コンポーネントのメッセージ(イベント)を定義
// Msg::DrawFortuneがおみくじを引くイベント
enum Msg {
DrawFortune,
}
// Modelに対してComponentトレイトを実装
impl Component for Model {
// メッセージの型を指定
type Message = Msg;
// プロパティの型を指定(本例ではプロパティを持たない)
type Properties = ();
// コンポーネントの初期化処理
// React のクラスコンポーネントの constructor に相当
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Self {
link,
fortune: String::from("おみくじを引いてみてください!"),
}
}
// メッセージ(イベント)が発生した際の処理
// React の componentDidUpdate に相当
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
// DrawFortuneイベントが発生した場合
Msg::DrawFortune => {
// 乱数生成器を初期化
let mut rng = rand::thread_rng();
// おみくじの結果の候補を定義
let fortunes = vec!["大吉", "中吉", "小吉", "吉", "凶"];
// 乱数を使って候補からおみくじの結果を選択し、fortuneに格納
self.fortune = fortunes[rng.gen_range(0..fortunes.len())].to_string();
// コンポーネントの再描画を指示
true
}
}
}
// プロパティが変更された際の処理
// React の componentWillReceiveProps に相当
// 本例ではプロパティを持たないので、常にfalseを返す
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
false
}
// コンポーネントの描画処理
// React の render に相当
fn view(&self) -> Html {
// JSXライクなマクロを使ってHTMLを生成
html! {
<div>
// ボタンをクリックした際のイベントハンドラを登録
<button onclick=self.link.callback(|_| Msg::DrawFortune)>{ "おみくじを引く" }</button>
// おみくじの結果を表示
<p>{ &self.fortune }</p>
</div>
}
}
}
// WebAssemblyのエントリポイントを定義
#[wasm_bindgen(start)]
pub fn run_app() {
// ModelコンポーネントをDOMツリーにマウント
App::<Model>::new().mount_to_body();
}
このコードはWebAssemblyを使用してブラウザで実行されます。#[wasm_bindgen(start)]アトリビュートが付けられたrun_app関数が、アプリケーションのエントリーポイントとして機能します。この関数が呼び出されると、Yewアプリケーションが起動し、Modelがルートコンポーネントとしてレンダリングされます。
次にstaticフォルダを作成し、その中にindex.htmlファイルを追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Yew Fortune App</title>
<script type="module">
import init from "./wasm.js";
init();
</script>
</head>
<body></body>
</html>
-
<script type="module">:type="module"属性は、このスクリプトがES Modulesであることを示します。 -
import init from "./wasm.js";:init関数をwasm.jsファイルからインポートします。wasm.jsはアプリケーションをビルドする際に生成されます。 -
init();:インポートしたinit関数を呼び出します。この関数でWebAssemblyアプリケーションを初期化します。
これでシンプルな「おみくじ」アプリケーションのコードが書けました。次は実際にビルドして動作確認します。
5. アプリケーションの実行と表示
最後に、アプリケーションをビルドしてブラウザで実行・表示します。
まず、以下のコマンドでWebAssemblyパッケージをビルドし、./staticディレクトリにwasmという名前で出力します。
wasm-pack build --target web --out-name wasm --out-dir ./static
-
wasm-pack build:wasm-packツールを使ってWebAssemblyパッケージをビルドします -
--target web: ビルドターゲットをWebに設定し、ブラウザで直接実行できるよう出力します -
--out-name wasm: 出力ファイル名をwasm(wasm.js)に設定します -
--out-dir ./static: ビルド成果物の出力ディレクトリを./staticに設定します
次に、Rustの最新の開発版ツールチェーンをインストールし、それを使ってminiserveをインストールします。
rustup toolchain install nightly-x86_64-pc-windows-msvc
cargo +nightly install miniserve
-
rustup toolchain install nightly-x86_64-pc-windows-msvc: Rustの最新開発版ツールチェーン(nightly)を64bit Windowsに合わせてインストールします -
cargo +nightly install miniserve:miniserveという小さな高速HTTPファイルサーバをインストールします。+nightlyフラグで開発版ツールチェーンを使用するよう指示しています
最後に、./staticディレクトリをホストします。
miniserve ./static --index index.html
-
miniserve ./static:miniserveを使って./staticディレクトリをホストします -
--index index.html: サーバのデフォルトインデックスページをindex.htmlに設定します
これで http://127.0.0.1:8080 にアクセスすると、作成した「おみくじ」アプリが表示されます。

「おみくじを引く」ボタンを押すと、ランダムにおみくじの結果が表示されます。
以上で、RustとYewを使ったWebアプリケーションの作成が完了しました。
関数型コンポーネントを使ってみる
Yewの最新版では、Function Componentsと呼ばれる新しい形式のコンポーネントが導入されています。これはReactのFunction Componentsに似ており、よりシンプルで直感的なコードを書くことができます。
1. Yewのバージョンを上げる
Yewのバージョンを0.17からFunction Componentsがサポートされる0.19にバージョンアップします。Cargo.tomlを次のように修正します。
yew = "0.19"
2. 「おみくじ」アプリケーションを修正する
src/lib.rsファイルを以下のように編集します。
use rand::Rng;
use wasm_bindgen::prelude::*;
use yew::prelude::*;
// Reactの関数コンポーネントに相当します。Fortuneという名前の関数コンポーネントを定義します。
#[function_component(Fortune)]
fn fortune() -> Html {
// ReactのuseStateに相当します。おみくじの結果を保持する状態を作成します。
let fortune = use_state(|| String::from("おみくじを引いてみてください!"));
// ReactのuseCallbackに相当します。おみくじを引くためのコールバック関数を定義します。
let draw_fortune = Callback::from({
let fortune = fortune.clone();
move |_| {
let mut rng = rand::thread_rng();
let fortunes = vec!["大吉", "中吉", "小吉", "吉", "凶"];
fortune.set(fortunes[rng.gen_range(0..fortunes.len())].to_string())
}
});
// ReactのJSXに相当します。HTMLを返します。
html! {
<div>
<button onclick={draw_fortune}>{ "おみくじを引く" }</button>
<p>{ &*fortune }</p>
</div>
}
}
// ReactのReactDOM.renderに相当します。アプリケーションを起動するための関数を定義します。
#[wasm_bindgen(start)]
fn run_app() {
// Yewアプリケーションを起動します。Fortuneコンポーネントがルートコンポーネントとして使用されます。
yew::start_app::<Fortune>();
}
先ほどと同様にビルドとホスティングを行うことができます。
wasm-pack build --target web --out-name wasm --out-dir ./static
miniserve ./static --index index.html
このように、Function Componentsを使うと、コンポーネントのロジックをひとまとめにできるので、コードがシンプルになり可読性が向上します。
ReactのFunction Componentsと同様に、フックも利用できます。この例ではuse_stateフックを使って状態管理を行っています。
Function Componentsの導入により、Yewのコンポーネント記述がより直感的で分かりやすくなりました。
Discussion