ロゴ ロゴ

WebAssemblyでHello, World! [Rust]

どうも、二週間に一回ブログ更新という目標を早くも諦めたWhiteShirtです。一か月に一回でいいか……。

WebAssemblyとは?

WebAssembly(以下WASM)JavaScriptの改良版として開発が進められている実行可能なバイナリコードです、専用の仮想マシンで実行します。C、C++、Rustなどでソースコードを書き、それをWASMにコンパイルして使います。

WebAssemblyのメリット

JavaScriptと比べて以下のような利点があります。
– 事前にコンパイルするので動作が高速
– バイナリコードなのでサイズが小さい
– 静的型付け言語で開発できる

0.事前準備

今回はRustを使ってHello Worldします。インストールはここから

wasm-packが必要なのでインストールします。

Node.jsも必要です。

以下のコマンドを打ってnpmをアップデートしておきます。

npm install npm@latest -g

1.新規プロジェクトを作成

cargo new hello_world --lib

2.Cargo.tomlファイルの書き換え

[package]
name = "hello_world"
version = "0.1.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.75"

wasm-bindgenのおかげでWASMとJavascriptの連携が凄く楽になります。

3. lib.rsの書き換え

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

3..6: wasm-bindgenを使い、Javascriptのalert関数をexportします。
8..11: exportした関数を使用した関数を定義します。

4.package.jsonを作成

{
  "scripts": {
    "build": "webpack",
    "serve": "webpack-dev-server"
  },
  "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "1.0.1",
    "text-encoding": "^0.7.0",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.29.4",
    "webpack-cli": "^3.1.1",
    "webpack-dev-server": "^3.1.0"
  }
}

必要なパッケージを書いときます。

5.webpack.config.jsを作成

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");

module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'index.js',
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'index.html'
        }),
        new WasmPackPlugin({
            crateDirectory: path.resolve(__dirname, ".")
        }),
        // Have this example work in Edge which doesn't ship `TextEncoder` or
        // `TextDecoder` at this time.
        new webpack.ProvidePlugin({
          TextDecoder: ['text-encoding', 'TextDecoder'],
          TextEncoder: ['text-encoding', 'TextEncoder']
        })
    ],
    mode: 'development'
};

エントリーポイントなど諸々書いときます。呪文みたいなもんです。

6.htmlファイル, jsファイルを作成

最後にindex.htmlとWASMを呼び出すためのindex.jsを作ります。

htmlは適当に書きます。ひな形のようなもので構いません。大切なのはindex.jsの方です。

const rust = import('./pkg');

rust
  .then(m => m.greet('World'))
  .catch(console.error);

先ほど書いたRustをコンパイルするとpkgというフォルダが作られ、その中にWASMファイルができるので、そいつをimportし、呼び出します。

7.実行!!

まずpackage.jsonに書いたパッケージをインストールします。

npm install

そしたら実行します。

npm run serve

たったこれだけです。このコマンドを実行するとlib.rsファイルがWASMにコンパイルされ、さらにローカルサーバが立ち上がります。すごい。

そしたら http://localhost:8080/ にアクセスして結果を確認できます。Hello, Worldと表示されます。

先ほど書いたindex.jsファイルを以下のように変えると、ウェブページが自動でリロードされて、結果が変わります。

const rust = import('./pkg');

rust
  .then(m => m.greet('WASM'))
  .catch(console.error);

感想

WegGLとか使って綺麗なwebコンテンツを作れたらさぞかし楽しいだろうなと思いました。頑張らなくては……。

追記

WASMのプロジェクト作るたびに準備するのは面倒なので、テンプレート作りました。Minimum WebAssembly project for rust

参考

wasm-bindgenのサンプルコード集

wasm-packのドキュメント

コメント入力

関連サイト