メインコンテンツにスキップ
バージョン: 0.21

wasm-bindgen

wasm-bindgen は、Wasm モジュールと JavaScript の間の高レベルなインタラクションを容易にするためのライブラリおよびツールです。これは、The Rust and WebAssembly Working Group によって Rust で構築されています。

Yew は、いくつかのクレートを通じてブラウザとインタラクトするために wasm-bindgen を使用します。

このセクションでは、これらのクレートの一部を大まかに探求し、Yew で wasm-bindgen API を理解しやすく、使いやすくします。 wasm-bindgen とそれに関連するクレートの詳細なガイドについては、The wasm-bindgen Guide を参照してください。

上記のクレートのドキュメントについては、wasm-bindgen docs.rs を参照してください。

ヒント

wasm-bindgen の doc.rs 検索を使用して、wasm-bindgen を使用してインポートされたブラウザ API と JavaScript 型を見つけてください。

wasm-bindgen

このクレートは、上記の他のクレートの残りの部分の多くの構成要素を提供します。このセクションでは、wasm-bindgen クレートの 2 つの主要な領域、つまりマクロと、何度も表示される一部の型/トレイトのみを取り上げます。

#[wasm_bindgen] マクロ

#[wasm_bindgen] マクロは、Rust と JavaScript の間にインターフェースを提供し、2 つの間で翻訳を行うシステムを提供します。このマクロを使用することはより高度であり、外部 JavaScript ライブラリを使用しようとする場合を除き、手を伸ばす必要はありません。 js-sys および web-sys クレートは、組み込みの JavaScript 型およびブラウザ API の wasm-bindgen 定義を公開します。

#[wasm_bindgen] マクロを使用して、console.log 関数のいくつかの特定のフレーバーをインポートする簡単な例を見てみましょう。

use wasm_bindgen::prelude::*;

// First up let's take a look of binding `console.log` manually, without the
// help of `web_sys`. Here we're writing the `#[wasm_bindgen]` annotations
// manually ourselves, and the correctness of our program relies on the
// correctness of these annotations!
#[wasm_bindgen]
extern "C" {

// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);

// The `console.log` is quite polymorphic, so we can bind it with multiple
// signatures. Note that we need to use `js_name` to ensure we always call
// `log` in JS.
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);

// Multiple arguments too!
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
}

// using the imported functions!
log("Hello from Rust!");
log_u32(42);
log_many("Logging", "many values!");

この例は、The wasm-bindgen Guide の 1.2 Using console.log を参考にしました.

継承のシミュレーション

JavaScript クラス間の継承は、JavaScript 言語のコア機能であり、DOM (Document Object Model) はそれに基づいて設計されています。 wasm-bindgen を使用して型がインポートされる場合、継承を記述する属性を追加することもできます。

Rust では、この継承は Deref および AsRef トレイトを使用して表現されます。例を挙げると、3 つの型 ABC があり、CB を拡張し、次に BA を拡張するとします。

これらの型をインポートする場合、#[wasm_bindgen] マクロは次の方法で Deref および AsRef トレイトを実装します

  • CDerefB に指定できます
  • BDerefA に指定できます
  • CAsRefB に指定できます
  • C および B の両方を AsRefA に指定できます

これらの実装により、C のインスタンスで A のメソッドを呼び出し、C&B または &A であるかのように使用できます。

#[wasm-bindgen] を使用してインポートされたすべての単一の型は、同じルート型を持っていることに注意することが重要です。上記の例では A と考えることができます。この型は JsValue であり、以下のセクションがあります。

The wasm-bindgen Guide の extends セクション

JsValue

これは JavaScript が所有するオブジェクトの表現であり、wasm-bindgen のルートのキャッチオール型です。 wasm-bindgen から来た型はすべて JsValue です。これは、JavaScript には強力な型システムがないため、変数 x を受け入れるすべての関数は、その型を定義しないため、x は有効な JavaScript 値になる可能性があるため、JsValue です。 JsValue を受け入れるインポートされた関数または型を操作している場合、インポートされた値はすべて*技術的に*有効です。

JsValue は関数で受け入れることができますが、その関数は特定の型のみを受け入れる可能性があり、これによりパニックが発生する可能性があります。そのため、生の wasm-bindgen API を使用する場合は、その値が特定の型ではない場合に例外 (パニック) が発生するかどうかについて、インポートされている JavaScript のドキュメントを確認してください。

JsValue のドキュメント.

JsCast

Rust には強力な型システムがあり、JavaScript には...ありません 😞。Rust がこれらの強力な型を維持しながらも便利であるために、WebAssembly グループは非常に優れたトレイト JsCast を考案しました。その役割は、ある JavaScript 「型」から別の型に移行するのを助けることです。これは曖昧に聞こえるかもしれませんが、ある型が別の型であることを知っている場合、JsCast の関数を使用してある型から別の型にジャンプできることを意味します。 web-syswasm_bindgenjs-sys を操作するときに知っておくと便利なトレイトです。これらのクレートから多くの型が JsCast を実装していることに気づくでしょう。

JsCast は、チェックされたキャストとチェックされていないキャストの両方のメソッドを提供します。そのため、実行時に、特定のオブジェクトがどの型であるかわからない場合は、キャストを試すことができます。キャストは OptionResult のような失敗の可能性のある型を返します。

web-sys でのこの一般的な例は、イベントのターゲットを取得しようとしている場合です。ターゲット要素が何であるかを知っているかもしれませんが、web_sys::Event API は常に Option<web_sys::EventTarget> を返します。メソッドを呼び出せるように、要素の型にキャストする必要があります。

// need to import the trait.
use wasm_bindgen::JsCast;
use web_sys::{Event, EventTarget, HtmlInputElement, HtmlSelectElement};

fn handle_event(event: Event) {
let target: EventTarget = event
.target()
.expect("I'm sure this event has a target!");

// maybe the target is a select element?
if let Some(select_element) = target.dyn_ref::<HtmlSelectElement>() {
// do something amazing here
return;
}

// if it wasn't a select element then I KNOW it's a input element!
let input_element: HtmlInputElement = target.unchecked_into();
}

dyn_ref メソッドは、チェック済みのキャストで、Option<&T> を返します。これは、キャストが失敗して None を返した場合に、元の型を再度使用できることを意味します。 dyn_into メソッドは、Rust の into メソッドの規則に従って self を消費し、返される型は Result<T, Self> です。キャストが失敗すると、元の Self 値が Err で返されます。元の型で再度試行するか、他の処理を実行できます。

JsCast のドキュメント.

Closure

Closure 型は、Rust クロージャを JavaScript に転送する方法を提供します。JavaScript に渡されるクロージャは、健全性の理由から 'static ライフタイムを持つ必要があります。

この型は、破棄されるたびに参照する JS クロージャを無効にするという意味で「ハンドル」です。 Closure がドロップされた後に JS でクロージャを使用すると、例外が発生します。

Closure は、&js_sys::Function 型を受け入れる js-sys または web-sys API を操作する際に頻繁に使用されます。Yew での Closure の使用例は、イベントページのClosure を使用するセクションにあります。

Closure のドキュメント.

js-sys

js-sys クレートは、JavaScript の標準の組み込みオブジェクト(それらのメソッドとプロパティを含む)のバインディング/インポートを提供します。

これは、web-sys が担当するため、Web API は含まれません!

js-sys のドキュメント.

wasm-bindgen-futures

wasm-bindgen-futures クレートは、JavaScript の Promise 型を Rust の Future として操作するためのブリッジを提供し、Rust の Future を JavaScript の Promise に変換するユーティリティが含まれています。これは、Rust (wasm) で非同期処理やブロッキング処理を行う際に役立ち、JavaScript のイベントや JavaScript の I/O プリミティブとの相互運用性を提供します。

このクレートには現在、3 つの主要なインターフェースがあります。

  1. JsFuture - Promise で構築される型であり、Future<Output=Result<JsValue, JsValue>> として使用できます。この Future は、Promise が解決された場合は Ok に、Promise が拒否された場合は Err に解決され、それぞれ Promise から解決または拒否された値が含まれます。

  2. future_to_promise - Rust の Future<Output=Result<JsValue, JsValue>> を JavaScript の Promise に変換します。Future の結果は、JavaScript で解決済みまたは拒否済みの Promise のいずれかに変換されます。

  3. spawn_local - 現在のスレッドで Future<Output = ()> を生成します。これは、JavaScript に送信せずに Rust で Future を実行する最良の方法です。

wasm-bindgen-futures のドキュメント.

spawn_local

spawn_local は、非同期 API を持つライブラリを使用する際に役立つため、Yew において wasm-bindgen-futures クレートで最もよく使用される部分になるでしょう。

use web_sys::console;
use wasm_bindgen_futures::spawn_local;

async fn my_async_fn() -> String { String::from("Hello") }

spawn_local(async {
let mut string = my_async_fn().await;
string.push_str(", world!");
// console log "Hello, world!"
console::log_1(&string.into());
});

Yew は特定の API での Future のサポートも追加しており、特に async ブロックを受け入れる callback_future を作成できます。これは内部的に spawn_local を使用します。

spawn_local のドキュメント.