はじめに

本記事はProgaku Advent Calendar 2022 2日目担当です。
みなさん、WebAssembly(以下wasm)はご存知でしょうか? MDNのWebAssemblyに書かれている通り、簡単に言うとコンパイル済みのバイナリファイルをブラウザ上で実行できる技術です。
ますます高まるwebアプリケーションへの要求に対して、wasmの技術は必要なものになってくると推測できます。
そういうのもありアドベントカレンダーのいい機会でもありますので、アウトプットも兼ねて触れてみました。
よろしくお願いします。

目的

タイトル通り。Rustで書かれたUUID生成処理をwasmでTypeScriptから動かします。

環境

    • M1 Mac

 

    • npm v8.5.0

 

    rustc v1.65.0

環境構築

Rustサイド

install

$ brew install rustup-init
$ rustup-init
$ rustup target add wasm32-unknown-unknown

確認します。

$ rustup --version
$ rustc --version
$ cargo --version

必要ツールのインストールとプロジェクトの作成を行います。

$ cargo isntall wasm-bindgen
$ cargo new --lib wasm

TypeScriptサイド

$ npm init -y
$ npm install ts-node
$ npx tsc --init

ts-nodeで動作を確認します。
tsconfigは生成時そのままでも構いません。

プロダクト

ディレクトリ構造

名前もディレクトリ構造も雑ですが目をつぶってください。

.
├── apps
│   └── main.ts
├── node_modules
│   └── ...
├── package-lock.json
├── package.json
├── tsconfig.json
└── wasm
    ├── Cargo.lock
    ├── Cargo.toml
    └── src
        └── lib.rs

Rustサイド

Cargo.toml

Cargo.toml に以下の通り記載します。

[package]
name = "wasm"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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

[dependencies]
serde = "1.0.147"
serde_json = "1.0.89"
uuid = { version = "1.2.2", features = ["v4", "serde"] }
wasm-bindgen = "0.2.83"
getrandom = { version = "0.2", features = ["js"] }

uuidの生成はCrate uuidを用います。
これをwasmで使うには getrandom が必要になるため加えています。

参考: Usage with wasm-pack?

JSONパッケージがある理由は後述します。

実装

wasm/src/lib.rs に記載します。

use uuid::Uuid;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {}

#[wasm_bindgen]
pub fn uuid(n: i32) -> String {
    let mut uuids = vec![];
    for _ in 0..n {
        uuids.push(Uuid::new_v4().to_string());
    }
    return serde_json::to_string(&uuids).unwrap();
}

wasmではVec型を return できないのでjsonでダンプします。これがJSONパッケージを入れる理由となります。
せっかくなので引数も取れるようにしました。UUIDの生成数を与えます。

参考: wasmのRustではVec型をreturnできない

build

$ wasm-pack build --target nodejs

今回はts-nodeで動作確認をするため、ターゲットを nodejs とします。

参考: ビルドターゲット

TypeScriptサイド

apps/main.ts

import { uuid } from '../wasm/pkg/wasm';

const wasmUuid = uuid(10)

console.log(wasmUuid);
/**
* '[
*   "c7a2db39-4343-450e-bd99-8f8e33336260",
*   "5701aa3a-ffbe-435d-9fe5-d90903d99378",
*   "2086290a-c118-4e50-8270-368884fcf874",
*   "e466230f-428e-4305-b370-9f5314af95fb",
*   "b4c642ce-c47a-4940-a81a-bc1d115b711c",
*   "e7c556ef-3c90-4f73-8e10-e71c44fae295",
*   "32ad87c1-0fd9-4884-9756-919ae22dc71a",
*   "a0c12d0b-11f5-4d5b-bfdc-e1b70cf249ad",
*   "d0632205-ab34-4575-96df-20fb65638a01",
*   "174dbdd3-9840-4d0e-b623-e8d30d1142ab"
* ]'
*/
console.log(typeof wasmUuid);  // string

返却値は見やすいように整形してあります。また、importは上に記載したディレクトリ構造での相対パスとなっております。ご注意ください。
上記の通り、wasmサイドからの返却値はstring型になっているため、 JSON.parse()をするなどして扱いましょう。

おわりに

読んで頂きありがとうございました。
今はwasmといえばRustかC++が真っ先に上がるかと思いますが、RubyやKotlinもwasm技術に進出しています。
現在wasmを学ぶにあたって情報が多いのがRustだったためRustを選択しましたが、今後は得意な言語で書けるようになるでしょう。
先を見据えて今のうちに触れておくのも生存戦略としてもありかもですね。
以上、Progaku Advent Calendar 2022 2日目でした。

参考

    • Rust から WebAssembly にコンパイルする

 

    Node.js with TypeScriptでWeb Assembly使ってみる
广告
将在 10 秒后关闭
bannerAds