はじめに
本連載では、Webフロントエンド開発において注目されているバイナリ形式のプログラムファイルWebAssemblyを、Rustで実装して活用する方法を説明しています。初回記事ではWebAssemblyの背景およびRust開発環境の作成について、前回記事ではRust言語の基本的な言語仕様について説明しました。
今回はこれまで説明した内容を利用して、実際にRust言語でWebAssemblyを実装していきます。簡単なサンプルを作りながら、実装の手順やWebAssemblyのメリットなどを説明します。
対象読者
- 動的なWebページにさらなる速度を求める方
- RustやWebAssemblyの概要を知りたいフロントエンドエンジニアの方
- WebAssemblyを利用するメリットを実感したい方
必要な環境
本記事のサンプルコードは、以下の環境で動作を確認しています。
- Windows 10 64bit版
- Microsoft Edge 100.0.1185.36
- rustup 1.24.3
- cargo 1.60.0
- wasm-pack 0.10.2
- Node.js v16.14.2 64bit版
サンプルコードには、WebAssemblyをビルドするRustのプロジェクトと、ビルドしたWebAssemblyを利用してWebページを表示するプロジェクト(プロジェクト名の末尾が「-web」のもの)が含まれます。前者は「wasm-pack build」コマンドでビルドできます。後者を実行するには、WebAssemblyプロジェクトをビルドして生成した「pkg」フォルダーをサンプルのフォルダーにコピーした後、「npm install」コマンドでライブラリーをインストールします。その後「npm run start」コマンドを実行して、Webブラウザーで「http://localhost:8080/」を表示します。
繰り返し処理で円周率を計算するサンプル
WebAssemblyと言えば、重い計算処理を高速で行うイメージを持つ人が多いのではないでしょうか。そこで本記事ではまず、図1に示す「ライプニッツの公式」で円周率を計算するサンプルを紹介します。この公式では、分数の加算処理を繰り返すことで、円周率を求められます。
なお、円周率を求める方法には、より計算効率や精度の高いものが存在しますが、本記事では比較的処理が単純で、かつ、計算量の多い例として本公式を使用していきます。
ダウンロードできるサンプル(p001-calc-pi-web)を実行すると、図2の通り、円周率を計算してWebページのダイアログ(alert)で表示します。
以下では図2のサンプルを、Rust/WebAssemblyで作っていきます。Rust/WebAssembly開発環境の作り方は、本連載の初回記事も参考にしてください。
まず、ライプニッツの公式をRustで実装するプロジェクトを、リスト1のコマンドで生成します。
cargo generate --git https://github.com/rustwasm/wasm-pack-template
処理を実装するファイルは、生成されたプロジェクト内のsrc/lib.rsです。円周率を計算するライプニッツの公式は、Rustでリスト2の通り実装できます。
#[wasm_bindgen] // ...(1) pub fn calc_pi(count: u64) -> f64 { // ...(2) let mut i = 1_u64; // ...(3) let mut value = 1.0_f64; // ...(4) while i <= count { // ...(5) let index = 2 * i - 1; value = value - 1.0 / ((2 * index + 1) as f64) + 1.0 / ((2 * (index + 1) + 1) as f64); i += 1; } return value * 4.0; // ...(6) }
(1)の#[wasm_bindgen]は、WebAssemblyの関数を呼び出すJavaScriptのメソッドを生成することを表す記述です。(2)で、u64(符号なし64ビット整数)の計算回数countを引数にとり、f64(64ビット浮動小数点数)で円周率を返却する関数calc_piを定義します。関数の中では、まず(3)でu64型の変数i、(4)でf64型の変数valueを定義します。「let mut」は変数が変更できることを表し、「1_u64」や「1.0_f64」は、変数の初期値と型を表します。(5)のwhileループでライプニッツの公式に基づいた処理を行い、計算された円周率を(6)で返却します。Rustの文法については、前回記事も参考にしてください。
リスト2の実装後、リスト3のコマンドでWebAssemblyをビルドします。ビルド実行後、WebAssemblyと、それを呼び出すためのJavaScriptファイルなど一式が、プロジェクトのpkgフォルダーに生成されます。
wasm-pack build
次に、WebAssemblyを実行するWebページのプロジェクト(p001-calc-pi-web)を、リスト4のコマンドで生成します。
npm init wasm-app <プロジェクト名>
生成されたプロジェクトのフォルダーに、WebAssemblyプロジェクトをビルドして生成されたpkgフォルダーをコピー後、package.jsonにリスト5の通り記述して、pkgフォルダーの内容をパッケージとしてJavaScriptから参照できるようにします。
"dependencies": { "p001-calc-pi": "file:./pkg" },
Webページ表示時に実行されるindex.jsファイルに、WebAssemblyの円周率計算処理を呼び出す実装を、リスト6の通り行います。リスト5で記述したWebAssemblyのパッケージを(1)でインポートして、そのcalc_piメソッドを(2)で呼び出して円周率を計算します。calc_piメソッドの引数で利用しているBigIntは、JavaScriptで巨大な整数を表すオブジェクトで、Rustのリスト2(2)で指定した64ビット整数に対応するために利用します。
// WASMをインポート ...(1) import * as wasm from 'p001-calc-pi'; // WASMのcalc_piメソッドで円周率を計算 ...(2) alert(wasm.calc_pi(BigInt(10000000)));
ここまで実装後、「npm install」コマンドでライブラリーをインストールして「npm run start」コマンドで実行後、Webブラウザーで「http://localhost:8080/」を表示すると、図2の通り円周率を計算できます。