--- title: Rust から WebAssembly にコンパイルする slug: WebAssembly/Rust_to_wasm tags: - WebAssembly - rust - wasm - コンパイル translation_of: WebAssembly/Rust_to_wasm ---
Rust のコードがあれば、それを WebAssembly (wasm) にコンパイルすることができます。このチュートリアルでは Rust プロジェクトをコンパイルして既存のウェブアプリケーションで使用するために必要なことについて説明します。
Rust と WebAssembly には、主に 2 つのユースケースがあります。
今のところ、Rust チームは後者のケースに焦点を当てているので、ここではこれについて説明します。前者の場合、yew
のようなプロジェクトをチェックアウトしてください。
このチュートリアルでは、Rust で npm パッケージを構築するためのツールである wasm-pack
を使用して npm パッケージを構築します。このパッケージには WebAssembly と JavaScript のコードしか含まれていないため、パッケージのユーザーは Rust をインストールする必要がありません。WebAssembly で書かれていることにすら気づかないかもしれません。
環境を整えるために必要なすべてのステップを踏んでみましょう。
Install Rust ページに行って指示に従い、Rust をインストールしてください。これによって "rustup" と呼ばれる複数のバージョンの Rust を管理できるようにするツールがインストールされます。既定の設定では、通常の Rust 開発で使いたいであろう最新の安定版 Rust リリースをインストールします。rustup は Rust コンパイラの rustc
や Rust のパッケージマネージャーの cargo
や Rust の標準ライブラリの rust-std
やいくつかの助けになるドキュメント — rust-docs
をインストールします。
メモ: インストール後のメモで、cargo の bin
ディレクトリーをシステムの PATH
に追加する必要があるという点に注意してください。これは自動的に追加されるはずですが、有効にするためにターミナルを再起動する必要があります。
パッケージをビルドするには、wasm-pack
という追加のツールが必要です。これは npm
向けに正しくパッケージングをすることだけでなく、WebAssembly にコードをコンパイルするのにも役立ちます。ダウンロードしてインストールするには、ターミナルに次のコマンドを入力します。
$ cargo install wasm-pack
このチュートリアルでは npm パッケージをビルドするので、Node.js と npm のインストールが必要になります。さらに、パッケージを npm にパブリッシュするので、npm アカウントも必要になります。それらは無料です。技術的にはパッケージをパブリッシュする必要はありませんが、そのほうが簡単に使用できるので、このチュートリアルではそうすると仮定します。
Node.js と npm を取得するために、Get npm! ページに行き、手順に従ってください。バージョンの選択に関しては、好きなバージョンを選択してください。このチュートリアルはバージョンに特有ではありません。
npm アカウントを取得するために、npm signup page に行き、フォームに記入してください。
次に、コマンドラインで npm adduser
を実行してください。
$ npm adduser Username: yournpmusername Password: Email: (this IS public) you@example.com
ユーザー名とパスワードとメールアドレスを記入してください。うまくいけば、以下の表示が見られます。
Logged in as yournpmusername on https://registry.npmjs.org/.
もし何かうまくいかなければ、トラブルシューティングのヘルプを得るために npm に連絡してください。
セットアップは以上です。Rust で新しいパッケージを作りましょう。個人的なプロジェクトを置いておく場所へ移動して以下を実行してください。
$ cargo new --lib hello-wasm Created library `hello-wasm` project
これにより新たなライブラリが出発に必要なものすべてと一緒に hello-wasm
という名前のサブディレクトリーに作成されます。
+-- Cargo.toml +-- src +-- lib.rs
まず Cargo.toml
があります。これはビルドを設定するためのファイルです。もし Gemfile
を Bundler から使ったり、package.json
を npm から使ったりしたことがあるなら、なじみがあるでしょう。cargo は両者と似たような動作をします。
次に、cargo はいくつかの Rust コードを src/lib.rs
に生成してくれています。
#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
このチュートリアルでは、このテストコードはまったく使わないので、消してください。
代わりに以下のコードを src/lib.rs
に書き込みましょう。
extern crate wasm_bindgen; use wasm_bindgen::prelude::*; #[wasm_bindgen] extern { pub fn alert(s: &str); } #[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); }
これが Rust プロジェクトの中身です。三つの主要な部分があります。順番に説明しましょう。ここでは高水準な説明を行い、細部は省略します。Rust についてもっと学びたいのであれば、無料のオンラインブック The Rust Programming Language (訳注: 和訳もあります) を確認してください。
wasm-bindgen
を使用して Rust と JavaScript を協調させる最初の部分は以下のようになっています。
extern crate wasm_bindgen; use wasm_bindgen::prelude::*;
1行目は「やあ Rust、wasm_bindgen
というライブラリを使ってるよ」ということです。ライブラリは Rust では「クレート」と呼ばれ、外部 (external) のクレートを使っているので extern
キーワードを使用しています。
理解できましたか? Cargo がクレートを取り入れるのです。
3行目にはコードをライブラリから自分のコードにインポートする use
コマンドがあります。この場合、wasm_bindgen::prelude
モジュールにあるものすべてをインポートしています。これらの機能は次の節で使用します。
次の節に移動する前に、もう少し wasm-bindgen
について話しておいたほうがいいでしょう。
wasm-pack
は 別のツールの wasm-bindgen
を利用して、JavaScript と Rust の型を繋いでいます。wasm-bindgen
によって JavaScript が文字列に関する Rust API を呼び出すことや Rust の関数が JavaScript の例外をキャッチすることができるようになります。
パッケージ内で wasm-bindgen
の機能を使うことになるでしょう。実際、次の節で利用します。
次の部分は以下のようになっています。
#[wasm_bindgen] extern { pub fn alert(s: &str); }
#[ ]
の内側は「アトリビュート」と呼ばれ、次に来る文を何らかの形で修飾します。この場合、その文は外部で定義された関数を呼び出したいことを Rust に伝える extern
です。アトリビュートは「wasm-bindgen はこれらの関数を見つける方法を知っている」ということを意味しています。
3行目は関数の Rust で書かれたシグニチャです。「alert
関数は s
という名前の引数を一つ取る」ということを意味しています。
お察しの通り、これは JavaScript によって提供される alert
関数です。次の節でこの関数を呼び出します。
JavaScript 関数を呼び出したい時はいつでも、このファイルに追加すれば、wasm-bindgen
があらゆるセットアップの世話をしてくれます。まだすべてに対応している訳ではありませんが、作業をしています。何か見つからないものがあればバグを報告してください。
最後の部分は以下のコードです。
#[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); }
再び #[wasm_bindgen]
アトリビュートが目に入ります。この場合、extern
ブロックではなく fn
を改変しています。これは JavaScript がこの Rust 関数を呼び出せるようにしてほしいということを意味します。これは extern
とは逆です。自分が必要とする関数ではなく、外の世界に渡す関数なのです。
この関数は greet
という名前で、引数に (&str
と書かれる) 文字列 name
を一つ取ります。そしてそれは上の extern
ブロックで要求した alert 関数を呼び出します。文字列を結合する format!
マクロに呼び出しを渡します。
format!
マクロはこの場合フォーマット文字列とそこに挿入する変数の二つの引数を取ります。フォーマット文字列は "Hello, {}!"
の部分です。それは変数が補完される {}
を含みます。渡している変数は関数の引数 name
なので、greet("Steve")
と呼び出すと "Hello, Steve!"
が見られるはずです。
これは alert() に渡されるので、この関数を呼び出すと "Hello, Steve!" と書かれたアラートボックスが現れるでしょう。
ライブラリを書いたので、それをビルドしましょう。
コードを正しくコンパイルするには、はじめに Cargo.toml
で設定する必要があります。Cargo.toml
を開き、以下のように中身を変更してください。
[package] name = "hello-wasm" version = "0.1.0" authors = ["Your Name <you@example.com>"] description = "A sample project with wasm-pack" license = "MIT/Apache-2.0" repository = "https://github.com/yourgithubusername/hello-wasm" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
自分自身のリポジトリを記入し、git
が authors
フィールドに使用するものと同じ情報を使用してください。
追加する大部分は下にあるものです。最初の部分 — [lib]
— は Rust にパッケージの cdylib バージョンをビルドするよう伝えます。何を意味するかはこのチュートリアルでは掘り下げません。もっと知るには、Cargo と Rust Linkage のドキュメンテーションを調べてください。
第二の項は [dependencies]
の項です。ここで Cargo にどのバージョンの wasm-bindgen
に依存させるかを知らせます。今回の場合、バージョン 0.2.z
のいずれかのものです (0.3.0
やそれ以上ではありません)。
すべてのセットアップが完了したので、ビルドしましょう。ターミナルに以下のものを入力してください。
$ wasm-pack build --scope mynpmusername
このコマンドは多くのことをします (そして時間がかかます。特に初めて wasm-pack
を実行したときはそうです)。それらについて詳しく学ぶには、Mozilla Hacks のこのブログ投稿を確認してください。手短に説明すると、wasm-pack build
は次のことをします。
wasm-bindgen
をその WebAssembly に対して実行し、WebAssembly ファイルを npm が理解できるモジュールにラップする JavaScript ファイルを生成する。pkg
ディレクトリーを作成し、その JavaScript ファイルと WebAssembly コードをそこに移動する。Cargo.toml
を読み、等価な package.json
を生成する。README.md
をパッケージにコピーする。最終的な結果は? npm パッケージが pkg
ディレクトリーに生成されます。
生成された WebAssembly のコードサイズについて確認すると、それはおそらく数百キロバイトでしょう。Rust にはサイズの最適化をまったく指示しておらず、最適化すればサイズを大幅に削減できます。これはこのチュートリアルの脱線ですが、もしもっと学習したいなら、Rust WebAssembly Working Group の.wasm のサイズの縮小を確認してください。
この新たなパッケージを npm レジストリに発行しましょう。
$ cd pkg $ npm publish --access=public
Rust で書かれ、WebAssembly にコンパイルされた npm パッケージができました。JavaScript から利用する準備ができており、ユーザーが Rust をインストールすることを必要としません。コードに含まれているのは WebAssembly コードであり、Rust のソースではないのです。
この新たなパッケージを利用するウェブサイトを構築しましょう。多くの人が様々なバンドラーツールで npm のパッケージを利用していますが、このチュートリアルではそのうちの一つである webpack
を使用します。これは若干複雑ですが、現実的なユースケースを示します。
pkg
ディレクトリーの外に戻り、新たなディレクトリー site
を作成し、そこでこれを試してみましょう。
$ cd ../.. $ mkdir site $ cd site
新しいファイル package.json
を作成し、次のコードをそこに書き込んでください。
{ "scripts": { "serve": "webpack-dev-server" }, "dependencies": { "@mynpmusername/hello-wasm": "^0.1.0" }, "devDependencies": { "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.10" } }
dependencies の項の @
の後に自分自身のユーザー名を記入する必要があることに注意してください。
次に、Webpack を設定する必要があります。webpack.config.js
を作成し、そこに次のことを記入してください。
const path = require('path'); module.exports = { entry: "./index.js", output: { path: path.resolve(__dirname, "dist"), filename: "index.js", }, mode: "development" };
さて HTML ファイルが必要です。index.html
を作成し、次の内容を追加してください。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello-wasm example</title> </head> <body> <script src="./index.js"></script> </body> </html>
最後に、HTML ファイルで参照される index.js
を作成し、以下の内容を追加してください。
const js = import("./node_modules/@yournpmusername/hello-wasm/hello_wasm.js"); js.then(js => { js.greet("WebAssembly"); });
また npm のユーザー名を記入する必要があることに注意してください。
これは新しいモジュールを node_modules
フォルダーからインポートします。これは最善の方法ではないと思いますが、デモなので、これでいいでしょう。一度そのモジュールが読み込まれると、そこから greet
関数を呼び出し、"WebAssembly"
を文字列として渡します。ここに特別なことはなにもありませんが、Rust コードを呼び出していることに注意してください。JavaScript コードから観察する限り、これはただの普通のモジュールです。
ファイルを作りました。これを試してみましょう。
$ npm install $ npm run serve
これでウェブサーバーが起動します。http://localhost:8080 を読み込んでください。Hello, WebAssembly!
と書かれたアラートボックスが画面に出てくるはずです。JavaScript からの Rust の呼び出しと Rust からの JavaScript の呼び出しに成功しました。
ここでチュートリアルは終わりです。あなたの役に立ったと思われることを望みます。
この領域にはたくさんの進行中の刺激的な仕事があります。もしそれをもっとよくするのを手伝いたいなら、Rust Webassembly Working Group を確認してください。