Rustを用いたWasmの執筆に関するノート
概要
- RustとWasmの連携におけるwasm-bindgen活用パターンの紹介
- 参照渡しや命名規則など、痛みを減らす実践的アドバイス
- Copyの禁止や**Rc<RefCell<T>>/Arc<Mutex<T>>**の推奨
- wasm_refgenやネーミング規則の重要性
- バインディングの設計とメモリ管理の注意点を解説
Rust + Wasm開発を快適にするwasm-bindgenパターン
- wasm-bindgenはRust構造体・関数をJS/TSから呼び出すためのグルーコード自動生成ツール
- RustとJavaScriptで異なるメモリ管理モデル(Rust:所有権/借用、JS:GC/非同期)を同時に扱う必要性
- wasm-bindgenの制約や落とし穴を理解し、実践的なパターンで開発効率向上
主な推奨パターン
- Wasm境界を超える値は&参照で渡す
- **&mutではなくRc<RefCell<T>>やArc<Mutex<T>>**を利用
- Copyトレイトはエクスポート型に付けない
- コレクションで境界を超える型はwasm_refgenを使う
- Rustエクスポート型はWasm*プレフィックス、js_name/js_classでJS側名統一
- JSインポート型はJs*プレフィックス
- Rustエラー型はFrom<YourError> for JsValueを実装しjs_sys::Errorを活用
命名規則
- Wasm*型:Rustからエクスポートする構造体・enumのラッパー
- 例:
pub struct WasmStreetLight(StreetLight) - Rust側でWasm*を付け、JS側ではそのままの名前で扱う
- 例:
- Js*型:JSからRustへインポートする型に付与
- 例:
type JsCharacter; - メソッド名も
js_*で接頭辞を付けることで命名衝突防止
- 例:
IntoWasmAbi型
- プリミティブ型(u32, String, Vec<u8>等)はIntoWasmAbiを実装し、特別な処理不要
Copy禁止の理由
- Copyを付与すると、リソースハンドルの重複コピーが発生し、ヌルポインタやメモリ破壊の原因
- 純粋なデータ型のみCopy許可、ハンドル型は必ずCloneのみ
ハンドル破壊防止
- Rust側で所有権を消費(move)してしまうと、JS側ハンドルが不正参照に
- 常に&参照で渡し、内部可変性(Rc<RefCell<T>>やArc<Mutex<T>>)で管理
- Wasm境界を超えた後も、JS/Rust双方で生存期間を意識
&mutの回避
- JSのasync/再入性により、Rustの&mutセーフティが保証されない
- 排他性が証明できない場合は、&mutではなく内部可変性プリミティブを利用
コレクション型の制約と回避策
- wasm-bindgenは&[T]やVec<&T>等、IntoWasmAbi未実装型のコレクション受け渡しを禁止
- Rc/ArcでCloneを安価にし、クローン用メソッドをエクスポートし、JS側で
.into()変換 - Duck TypingでJSインターフェースをRustにインポートし、柔軟な型受け渡し
マニュアルバインディングの是非
- js_sys等を使った手動変換は柔軟だが、型変更時にコンパイラの恩恵を受けにくく非推奨
- wasm-bindgenの自動生成グルーを活用することで、型安全性・保守性向上
エラー型の扱い
- Rustエラー型はFrom<YourError> for JsValueを実装し、js_sys::ErrorとしてJS側へ返却
- エラー伝播の一貫性とJS側での扱いやすさを確保
まとめ:Rust+Wasm開発の心得
- wasm-bindgenの制約とメモリ管理モデルの違いを理解
- 参照渡し・内部可変性・命名規則・エラー伝播のパターンを徹底
- JS/Rust間の型・リソース管理の明示的設計が重要
- これらのパターンを守ることで、Rust+Wasm開発の痛みを大幅に軽減可能