UUID生成で入門するRustでWebAssembly

# はじめに

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

# 目的

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

# 環境

- M1 Mac
- npm v8.5.0
- rustc v1.65.0

# 環境構築

## Rustサイド

install

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

確認します。

```sh
$ rustup --version
$ rustc --version
$ cargo --version
```

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

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

## TypeScriptサイド

```sh
$ 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` に以下の通り記載します。

```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](https://docs.rs/uuid/latest/uuid/)を用います。
これをwasmで使うには `getrandom` が必要になるため加えています。

参考: [Usage with wasm-pack?](https://github.com/rust-random/getrandom/issues/208)

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

### 実装

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

```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できない](https://qiita.com/ihcamonoihS/items/6fcd4594302605ba3b79)

### build

```sh
$ wasm-pack build --target nodejs
```

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

参考: [ビルドターゲット](https://rustwasm.github.io/docs/wasm-pack/commands/build.html#target)

# TypeScriptサイド

`apps/main.ts`

```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()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)をするなどして扱いましょう。

# おわりに

読んで頂きありがとうございました。
今はwasmといえばRustかC++が真っ先に上がるかと思いますが、[Ruby](https://github.com/ruby/ruby.wasm)や[Kotlin](https://blog.jetbrains.com/kotlin/2021/11/k2-compiler-kotlin-wasm-and-tooling-announcements-at-the-2021-kotlin-event/)もwasm技術に進出しています。
現在wasmを学ぶにあたって情報が多いのがRustだったためRustを選択しましたが、今後は得意な言語で書けるようになるでしょう。
先を見据えて今のうちに触れておくのも生存戦略としてもありかもですね。
以上、[Progaku Advent Calendar 2022](https://qiita.com/advent-calendar/2022/progaku) 2日目でした。

# 参考

- [Rust から WebAssembly にコンパイルする](https://developer.mozilla.org/ja/docs/WebAssembly/Rust_to_wasm)
- [Node.js with TypeScriptでWeb Assembly使ってみる](https://qiita.com/hirokoji/items/41221d9b13dbbb76cab3)