🍣
wasm-bindgen: Javascriptから呼び出せるRust関数を作ろう
wasm-bindgen: Javascriptから呼び出せるRust関数を作ろう
はじめに
Rustは高速な実行速度を持つプログラミング言語であり、WebAssembly(Wasm)を使うことで、ブラウザ上でもそのパフォーマンスを活かすことができる。具体的には、時系列データの統計処理などを高速に行うことが可能である。
wasm-bindgen
は、Rustで書いた関数をJavaScriptから呼び出せるようにするためのツールである。今回は文字列を返す関数をRustで書き、それをReactアプリケーションから呼び出す方法を紹介する。
なお、RustとReactについての基本的な知識があることを前提にしている。
この記事で話すこと
-
wasm-bindgen
の基本的な使い方 - Rustで書いた関数をJavaScriptから呼び出す方法
- オブジェクトの扱い方
この記事で話さないこと
-
wasm-bindgen
,Rust
以外の詳細(Reactなど)
セットアップ
Viteのセットアップ
pnpm create vite@latest
- プロジェクト名
react-wasm-helloworld
- フレームワーク
React
- TypeScriptを使用
wasm-packのインストール
cargo install wasm-pack
wasmのセットアップ
cargo new --lib wasm
hello worldを表示する
Rustで"Hello, World!"を返す関数を作成し、web-pack
を用いてWasm
にコンパイルする。その後、Reactアプリケーションからこの関数を呼び出して結果を表示する。
wasm/src/lib.rsを書き換える
wasm/src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet() -> String {
"Hello, World!".to_string()
}
wasmのビルド
- 設定
Cargo.toml
[package]
name = "wasm"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.100"
- ビルド
cd wasm
wasm-pack build --target web
Reactからwasmを呼び出す
- wasmをApp.tsxにインポート
src/App.tsx
import init, { greet } from '../wasm/pkg/wasm.js'
- wasmの初期化
src/App.tsx
useEffect(() => {
init()
}, [])
-
greet
関数を呼び出して表示
src/App.tsx
const res = greet()
// Hello, World!
console.log(res)
RustとTS間でオブジェクトをやり取りする
Tsify
というクレートを使うことで、オブジェクトのやり取りが型安全で行える。Tsify
はRustの構造体をTypeScriptの型に変換するためのツールであり、wasm-bindgen
と組み合わせて使用することで、RustとTypeScript間でオブジェクトをやり取りできるようになる。
クレートのインストール
cargo add serde
cargo add tsify
オブジェクトの定義
wasm/src/types.rs
use serde::{Deserialize, Serialize};
use tsify::Tsify;
#[derive(Tsify, Serialize, Deserialize)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct GreetResponse {
pub value: String,
#[tsify(optional)]
pub name: Option<String>,
}
-
Tsify
を使うことで、Rustの構造体からTypeScriptの型を生成できる -
#[tsify(optional)]
により、TypeScriptのオプショナルなプロパティを扱える
src/lib.rsの修正
wasm/src/lib.rs
mod types;
use types::GreetResponse;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: Option<String>) -> GreetResponse {
GreetResponse {
value: "Hello, World!".to_string(),
name,
}
}
Reactからの呼び出し
src/App.tsx
const res = greet()
const value = res.value
const name = res.name ?? ''
const message = `${value} ${name}`
// Hello, World!
console.log(message)
- 引数なしで呼び出すとnameは
null
になる
src/App.tsx
// const res = greet()
const res = greet('test')
const value = res.value
const name = res.name ?? ''
const message = `${value} ${name}`
// Hello, World! test
console.log(message)
- 引数を渡すとnameに値が入る
ビルド時に生成されている型
-
wasm/pkg/wasm.d.ts
にTypeScriptの型が生成されている
wasm/pkg/wasm.d.ts
export function greet(name?: string | null): GreetResponse;
export interface GreetResponse {
value: string;
name?: string;
}
ここで紹介したコード
https://github.com/k22036/react-wasm-helloworld
まとめ
-
wasm-bindgen
を使うことで、Rustで書いた関数をJavaScriptから呼び出せるようになる -
Tsify
を使うことで、RustとTypeScript間でオブジェクトのやり取りが型安全に行える
Discussion