SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Rust言語で作るWebバイナリファイル「WebAssembly」入門

RustではじめるWebAssembly入門~JavaScriptを超える高速なWebアプリ開発を実践しよう

Rust言語で作るWebバイナリファイル「WebAssembly」入門 第1回

  • X ポスト
  • このエントリーをはてなブックマークに追加

マイクロソフトの「Blazor」に見るWebAssemblyの利用例

 前述の通りWebAssemblyでは、さまざまな言語での開発が想定されています。ここではWebAssemblyの利用例として、マイクロソフトのWebページフレームワーク「Blazor」でWebAssemblyを利用する図5のサンプルを紹介します。画面左の「Counter」をクリックすると、「Click me」ボタンを押すごとにカウントが増えていくサンプルが表示されます。

図5 Blazor WebAssemblyのサンプル(p002-blazor-wasm-web)

図5 Blazor WebAssemblyのサンプル(p002-blazor-wasm-web)

 このサンプルは、Visual Studio 2019で「Blazor WebAssemblyアプリ」テンプレートで生成したプロジェクト(p002-blazor-wasm)をそのままビルドしたものです。

図6 プロジェクト生成時にBlazor WebAssembly アプリのテンプレートを選択

図6 プロジェクト生成時にBlazor WebAssemblyアプリのテンプレートを選択

 図5のサンプルは、リスト2の通り記述されています。この記法は「Razor コンポーネント」と呼ばれるもので、表示内容を表すHTMLと、動作を表すC#のコードを同じファイルに記述します。文法の細かい説明は省略しますが、ボタン押下時に(2)のIncrementCountメソッドが実行されて(1)のcurrentCount変数が1増え、それが画面に表示されます。

[リスト2]図5のサンプルの実装(p002-blazor-wasm/p002-blazor-wasm/Pages/Counter.razor)
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
    private int currentCount = 0; // カウンター ...(1)
    private void IncrementCount() // カウンターを増やすメソッド ...(2)
    {
        currentCount++;
    }
}

 p002-blazor-wasmプロジェクトはVisual Studioでそのまま実行できるほか、「発行」してWebサーバーにホストすることもできます。発行するには、ソリューション エクスプローラーでプロジェクトを右クリックして「発行」を選択し、表示された図7の発行先から「フォルダー」を選択します。

図7 プロジェクトの発行先で「フォルダー」を選択(p002-blazor-wasm)

図7 プロジェクトの発行先で「フォルダー」を選択(p002-blazor-wasm)

 p002-blazor-wasm-webサンプルコードは、p002-blazor-wasmプロジェクトからフォルダーに発行した成果物をNode.jsのWebサーバーで表示できるようにしたものです。wwwroot/_framework/_binフォルダーを見ると、プロジェクト内容や.NETライブラリーのアセンブリファイル(*.dll)が確認できます。

図8 発行されたプロジェクトに含まれる.NETアセンブリ(p002-blazor-wasm-web/wwwroot/_framework/_bin)

図8 発行されたプロジェクトに含まれる.NETアセンブリ(p002-blazor-wasm-web/wwwroot/_framework/_bin)

 Blazor WebAssemblyアプリでは、これらの.NETアセンブリを、サーバー側の.NETランタイムではなく、WebAssemblyで動作するWebブラウザー上の.NETランタイムで動作させます。.NETランタイムの実体は、wwwroot/_framework/wasmフォルダーに存在する「dotnet.wasm」です(WebAssemblyファイルの拡張子は「wasm」となります)。

図9 発行されたプロジェクトに含まれるWebAssemblyの.NETランタイム(p002-blazor-wasm-web/wwwroot/_framework/wasm)

図9 発行されたプロジェクトに含まれるWebAssemblyの.NETランタイム(p002-blazor-wasm-web/wwwroot/_framework/wasm)

 このようにWebAssemblyには、.NETランタイムそのものをWebブラウザー上で動作させられるほどの性能を備えています。

 [補足]dotnet.wasmなどの圧縮ファイルフォーマット

 図8、9では、ファイル名の末尾に「.br」と「.gz」がついたファイルが存在します。これらは各ファイルを圧縮したもので、「.br」のファイルは「Brotli」、「.gz」のファイルは「Gzip」という方法で圧縮されています。Webサーバーの設定によっては、これらの圧縮ファイルを利用して通信量を低減できます。

いよいよRustでWebAssembly実装にチャレンジ

 WebAssemblyで.NETランタイムを実現する図9のdotnet.wasmファイルは、サイズが約1.8MBと大きく、また内部的にガベージコレクションを行うため速度が落ちる懸念もあります。Rustを使えば、ランタイムやガベージコレクションのない、パフォーマンスやサイズが最適化されたWebAssemblyが作れます。

 ここからはいよいよ本連載の主題である、RustでWebAssemblyの実装を試していきます。今回は環境を作って最低限のサンプルコードを生成・実行し、Rustを利用したWebAssembly実装のイメージをつかんでいきます。

まずは環境作成

 RustとWebAssemblyの環境作成方法を説明します。まずRustの環境一式を整備できる「rustup」をインストールします。公式ページで、図10の赤枠部ボタンからインストーラーをダウンロードして実行します。

図10 rustupのダウンロードページ

図10 rustupのダウンロードページ(www.rust-lang.org/ja/tools/install

 次に、RustでWebAssemblyを作成する「wasm-pack」をインストールします。公式ページで、図11の赤枠部リンクから「wasm-pack-init.exe」をダウンロードして実行します。

図11 wasm-packのダウンロードページ

図11 wasm-packのダウンロードページ(rustwasm.github.io/wasm-pack/installer/

 WebAssembly対応のプロジェクトを生成するため、テンプレートからプロジェクトを生成するツール「cargo-generate」をインストールします。インストールするには、リスト3のコマンドを実行してください。なお「cargo」は、Rustのプロジェクト作成やビルド、コードチェックなどを行える、Rust開発の必須ツールです。詳細な利用法は次回以降説明します。

[リスト3]cargo-generateをインストールするコマンド
cargo install cargo-generate

 さらに、Node.jsをインストールして、npmコマンドが利用できるようにします。

RustのWebAssemblyプロジェクトを生成してビルド

 環境作成ができたら、いよいよプロジェクトを生成します。リスト4のコマンドを実行してください。ここではリスト3でインストールしたcargo-generateを利用して、--gitオプションに指定したGitHubリポジトリからプロジェクトを生成します。コマンドライン上でプロジェクト名の入力が求められるので、任意の名前を指定します(サンプルコードでは「p003-rust-wasm」としています)。

[リスト4]RustのWebAssemblyプロジェクトを生成するコマンド
cargo generate --git https://github.com/rustwasm/wasm-pack-template

 生成したプロジェクト内のsrc/lib.rsがWebAssemblyのRustソースコードファイルです(Rustソースコードの拡張子は「rs」となります)。

[リスト5]WebAssemblyを実装するRustファイル(p003-rust-wasm/src/lib.rs)
mod utils; // utils.rsを参照 ...(1)

use wasm_bindgen::prelude::*; // wasm_bindgen::preludeモジュールを利用 ...(2)

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.→メモリアロケーター ...(3)
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

// JavaScriptのalertメソッドを参照 ...(4)
#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

// greetメソッドの実装 ...(5)
#[wasm_bindgen]
pub fn greet() {
    alert("Hello, p003-rust-wasm!");
}

 (1)は、同じフォルダーに存在するutils.rsを参照する記述です。utils.rsにはRustのエラー発生時に詳細情報をJavaScriptのconsole.errorに表示する指定があります。(2)は、RustでWebAssemblyとJavaScriptを結び付ける「wasm_bindgen」のモジュールを利用する記述です。(3)は、Rustでメモリを割り当てるのに利用するメモリアロケーターの記述ですが、今回は利用しません(利用するには追加設定が必要です)。

 (4)と(5)が処理の実装です。(4)はRustからJavaScriptのalertメソッドを参照する記述で、(5)でそのalertメソッドでメッセージを表示するgreetメソッドを実装しています。「fn」は関数で、「pub」は関数を外部に公開することを表します。

 このプロジェクトをビルドするには、リスト6のコマンドを実行します。

[リスト6]WebAssemblyをビルドするコマンド
wasm-pack build

 ビルド実行後、プロジェクトのpkgフォルダーに、ビルドされたWebAssemblyファイル「p003_rust_wasm_bg.wasm」と、それをJavaScriptから利用するためのファイル群が生成されます。これらはnpmで公開されているパッケージと同じ形式となっており、ビルド成果物をnpmに公開することもできます(詳細は次回以降で紹介します)。

図12 ビルドされたパッケージの内容(p003-rust-wasm/pkg)

図12 ビルドされたパッケージの内容(p003-rust-wasm/pkg)

ビルドしたWebAssemblyのパッケージを参照して実行

 それでは、ビルドしたWebAssemblyのパッケージを実際に実行してみます。リスト7のコマンドを実行すると、npmでWebAssembly用のプロジェクトを自動生成できます。

[リスト7]WebAssembly用のプロジェクトを生成するコマンド
npm init wasm-app <プロジェクト名>

 生成したプロジェクトのルートフォルダーに図12のpkgフォルダーをコピー後、package.jsonに以下の通り記述して参照します。ここではpkgフォルダーのパッケージを「p003-rust-wasm」という名前で参照しています。

[リスト8]pkgフォルダーを参照する記述(p003-rust-wasm-web/package.json)
"dependencies": {
  "p003-rust-wasm": "file:./pkg"
},

 Webページ表示時に実行されるindex.jsに、以下の通り記述します。(1)でパッケージを参照して、(2)でそのgreetメソッドを実行しています。

[リスト9]WebAssemblyのメソッドを実行する記述(p003-rust-wasm-web/index.js)
import * as wasm from 'p003-rust-wasm'; // WebAssemblyパッケージを参照 ...(1)
wasm.greet(); // パッケージから参照したgreetメソッドを実行 ...(2)

 ここまで実装後「npm install」コマンドでライブラリーをインストールして「npm run start」メソッドを実行すると、リスト5(5)で実装したgreetメソッドにより、図13の通りダイアログが表示されます。

図13 WebAssemblyの処理で表示したダイアログ(p003-rust-wasm-web)

図13 WebAssemblyの処理で表示したダイアログ(p003-rust-wasm-web)

 今回は単にダイアログを表示しただけですが、Rustの実装からWebAssemblyを生成して、JavaScriptから利用できました。今後、WebAssemblyで提供したい機能をRustファイルlib.rs(リスト5)に実装していくことになります。

 [補足]index.jsを読み込むbootstrap.jsの役割

 リスト7のコマンドで生成するプロジェクトでは、index.jsを直接読み込まず、リスト10のbootstrap.jsで間接的に読み込んでいます。

[リスト10]index.jsを間接的に読み込む記述(p003-rust-wasm-web/bootstrap.js)
import("./index.js")
  .catch(e => console.error("Error importing `index.js`:", e));

 これは、WebAssemblyをインポートするモジュールは非同期で読み込む必要があるためで、動的インポートにより、importメソッドでindex.jsを読み込んでいます。

まとめ

 本記事では、RustでWebAssemblyを実装する方法を説明する連載の1回目として、WebAssemblyの概要と活用例を紹介しました。WebAssemblyはWebブラウザー上で動作するバイナリファイルで、記事の前半ではWebAssemblyで.NETランタイムを実行する例を紹介しました。後半では、実際にRustで簡単なWebAssemblyをビルドして実行するまでの手順を説明しました。

 次回は、実用的なWebAssemblyをRustで実装するために必要となる、基本的な開発方法や文法を説明していきます。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Rust言語で作るWebバイナリファイル「WebAssembly」入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト  吉川 英一(ヨシカワ エイイチ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/14567 2021/08/04 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング