こんにちは、平奥です!
朝の通勤時に TechCrunch を読むのですが、こんな記事を見つけました。
業務では C や C++ を使うことが多く、確かにここに書かれているような、ポインターの扱いを間違って致命的なバグを埋め込むこともありましたが、シンプルさと移植性の高さ、メモリーを直接参照できたり、実行速度も速く、何でも実現できるイメージがあり、好きな言語でした。
しかし時代は変わり、新しい言語 Rust が出てきて、C/C++ で書かれたプログラミングが置き換えられているんだなぁとつくづく時の流れを感じました。
そこから Rust に興味を持ち始めました。ドキュメントも充実していて公式サイトまた日本語サイトもあり、学習するには最適な環境が揃っています。
Rust フロントエンドで使えないかな
Rust フロントエンドで使えないかなとふと思いました。確か C や C++ は emscriptenで Javascript にコンパイルできたので、Rust もできるのではと。
Emscripten とは LLVM の bitcode を JavaScript に変換するコンパイラです。
調べてみると、Compiling to the web with Rust and emscripten というページがありました。
Emscripten が出力できるのは JavaScript だけではない
JavaScript 誕生当初は、使い方としては HTML に飾り付けをするような使い方で、手軽さが売りでした。しかし現在の Web アプリケーションでは複雑なアニメーションやグラフィック処理、処理速度の遅いモバイルにも対応しないといけなく、実行速度が求められるようになりました。各ブラウザベンダーも JavaScript エンジンをチューニングし、JavaScript は想像以上に高速に実行されるようになってはいますが、更なるパフォーマンスが求められるようになりました。
そこで登場してきたのが asm.js です。asm.js は JavaScript の言語仕様にある制約をつけ、型を明確にし事前にコンパイルできるようにする技術のことです。しかし asm.js にもファイルサイズが大きくなる、データ構造の概念がないなどの欠点がありました。
そこで登場してきたのが WebAssembly です。WebAssembly はブラウザでバイナリコードを実行することができる技術です。これによりファイルサイズが小さくなり、パフォーマンスを向上することができます。ただし欠点としてはまだ WebAssembly は発展途中で、DOM や WebAPI 、 GC などが使えません。ですので現状では重い処理などを WebAssembly で実装するような使い方になると思います。
将来は様々な機能が追加される予定のようです。興味のある方は 公式ページ などを見ていただいたらと思います。
Emscripten は asm.js、WebAssembly の両方とも出力することができます。
話は長くなりましたが、これから環境を構築して、実際に動作させたいと思います。環境は Linux で、今回は WebAssembly で動作させたいと思います。
まず rustup をインストール
rustup は rust の開発環境を作成するための便利ツールです。インストールは簡単で以下のコマンドを実行すればインストールできます。
1 |
curl https://sh.rustup.rs -sSf | sh |
WebAssembly をコンパイルできるようにターゲットを追加します。
1 |
rustup target add wasm32-unknown-emscripten |
Emscripten のインストール
次に Emscripten をインストールします。
1 2 3 4 5 6 |
$ curl -O https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz $ tar -xzf emsdk-portable.tar.gz $ source emsdk-portable/emsdk_env.sh $ emsdk update $ emsdk install sdk-incoming-64bit $ emsdk activate sdk-incoming-64bit |
インストールにはだいぶ時間がかかります。
サンプルソースを作成
サンプルソースを作成します。お決まりの Hello World で!
まず hello ディレクトリを作成します。
hello ディレクトリが作成されましたら、そのディレクトリに以下のファイルを追加します。
・main.rs
1 2 3 |
fn main() { println!("Hello, world!"); } |
コンパイルして実行
以下のコマンドでコンパイルします。
1 |
rustc --target=wasm32-unknown-emscripten main.rs -o hello.html |
コンパイルすると以下のようなファイルが生成されます。
実行
上記のディレクトリで Web サーバーを立ち上げれば実行することができます。
今回は Python で起動してみます。
1 |
python -m SimpleHTTPServer |
で Web サーバーを起動します。
それから 「http://localhost:8000/hello.html」でアクセスできます。
WebAssembly に対応しているブラウザでアクセスしてください。そうすると以下のような画面が表示されると思います。
と簡単に環境が構築でき、実行することができます。
Rust の関数を呼び出してみる
実行できる環境の構築ができたので、今度は Rust の関数を JavaScript 側から呼び出してみましょう!
まずは Rust のソースである main.rs を以下のように編集します。
1 2 3 4 5 6 |
fn main() {} #[no_mangle] pub extern fn count(n: i32) -> i32 { n + 1 } |
3行目の #[no_mangle] はコンパイラが名前を変更しないようにする機能です。
count は今回呼び出される関数です。引数で渡された値に1加算しているだけです。本当は重い処理を実装したかったのですが、まずは軽く処理で。みなさまはここの処理を、重い処理で実装すると WebAssembly のすごさを実感することができるかもしれません。
次に HTML ファイルを用意します。名前は main.html とします。
main.html の内容は以下となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Count <button class="mybutton">Count</button> <script> // グローバルで宣言する。 // Emscriptenで使用するModuleオブジェクト。 var Module = {}; var num = 0; fetch('main.wasm') .then((response) => response.arrayBuffer()) .then((buffer) => { // Module.wasmBinaryにmain.wasmの内容を書き込む。 Module.wasmBinary = buffer; // scriptタグを追加してmain.jsを実行する。 const scriptElem = document.createElement('script'); scriptElem.src = 'main.js'; document.body.appendChild(scriptElem); }); document.querySelector('.mybutton').addEventListener('click', function () { alert('check console'); var count = cwrap('count', 'number', ['number']); num = count(num) console.log(num); }); </script> |
軽く説明すると、17行目から27行目までで、wasm を Emscripten の Module で管理するよう登録しています。そのあとの24行目から26行目で script タグを生成し、main.js を追加しています。
11行目にボタンがありますが、このボタンをクリックしたときの処理が29行目から33行目です。ここでは、Rust で定義されている count 関数を呼び出しています。Emscripten の Module オブジェクトの cwrap メソッドを使って呼び出しを実現しています。
ソースの準備ができたので、コンパイルします。コンパイルは以下のコマンドを実行します。
1 |
rustc --target=wasm32-unknown-emscripten main.rs -o main.js -C link-args="-s EXPORTED_FUNCTIONS=['_count']" |
先ほど実行したコマンドより長くなっていますが、それは JavaScript 側で呼び出されるための関数名を指定しているためです。関数名の前にアンダースコアがつくことに注意してください。
今回は簡略化のためと説明のしやすさのためにここで指定していますが、ソースに記述したり、 cargo を利用して指定することも可能です。
準備ができたので、それではさっそく実行してみましょう!
Rust 関数呼び出しサンプルプログラムの実行
以前と同じように、Web サーバーを立ち上げます。
1 |
python -m SimpleHTTPServer |
今回はファイルが変わっているので「http://localhost:8000/main.html」でアクセスします。
そうすると以下のような画面が表示されると思います。
ここに表示されているボタンを押下します。それから Console を確認してください。以下のように表示されていると思います。
ちゃんと Rust の関数が呼び出されカウントされていることが確認できました!
まとめ
Rust はまだ新しい言語なので、資産などはあまりないと思いますが、C や C++ で書かれたコードは大量にあると思いますのでそれを移植し再利用することができるのではないかと思い、いろいろ試したくなりました。また WebAssembly はまだまだ発展途中なので、どんな機能が実装されるのか楽しみです。みなさまも興味を持たれたら試してみてください!それではまた!