ECMAScript の場合
例として次のように <input>
要素が記述された HTML があった場合、
<!-- .html -->
<input id="nyanko" />
上記に対して ECMAScript からは一般に window.document
のメンバーの getElementById
関数や querySelector
関数または今回の例のように id
に対してであればコードから見れば暗黙的に定義済みとなっている window
の .nyanko
メンバーを介して、
// .js
let nyanko = document.getElementById('#nyanko')
alert( nyanko.value ) // 👈 undefined ではなく '' が出力される
のように要素を取得し、 <input>
要素に固有の .value
メンバーを暗黙的に使用できる。 ECMAScript では "弱い動的型付け" によりプログラマーが理解していれば抽象型と具象型あるいは明示的な型変換などは気にせず簡潔な記述で目的の処理を記述しやすい。(もちろんその反面として、プログラマーがよく理解し注意深く記述しなければ不具合の温床となりうる切っ掛けに満ち溢れていたり、最適化がより難しいなどの課題も併せ持つ。)
Rust な WASM で web-sys の場合
先に示した ECMAScript の例に対して、 Rust から WASM で web-sys を介して同様の処理を行いたい場合、同様のコードを記述しようとすると、
// .rs
let w = web_sys::window().unwrap();
let d = w.document().unwrap();
let nyanko = d.get_element_by_id( "nyanko" ).unwrap();
w.alert_with_message( &nyanko.value() ).unwrap(); // 👈 翻訳時エラーとなる
となり、 Element
型には .value
は未定義なので翻訳時エラーとなる。Rustは強い静的型付けにより言語特性として原則的にプログラマーに抽象型と具象型あるいは明示的な型変換を意図して記述させ安全性の担保を図っている。
nyanko
変数として束縛している対象は「抽象的に何かしらの<????>
要素」を示す Element
型ではなく「具象的に<input>
要素」である HtmlInputElement
型として参照可能な事を明示的に記述しなければ先の ECMAScript の例と同様の期待動作は得られない。
// .rs
use wasm_bindgen::JsCast; // ⇩で使う .dyn_ref はこの trait に含まれる
use web_sys::HtmlInputElement; // Cargo.toml で web_sys の features に追加した上で使用する
let w = web_sys::window().unwrap();
let d = w.document().unwrap();
let nyanko = d.get_element_by_id( "nyanko" ).unwrap();
let nyanko = nyanko.dyn_ref::<HtmlInputElement>().unwrap(); // 👈 型変換
w.alert_with_message( &nyanko.value() ).unwrap(); // 👈 '' が出力される
これで先の ECMAScript の例と同様に期待動作する。
おまけ: getElementById
しつつ HtmlInputElement
など欲しい型を受け取る get_element_by_id_as
関数
前述の dyn_ref
は型変換した参照を取得するため、次のような関数で lifetime を扱う積極的な理由が無ければ dyn_into
で型変換する対象の所有権を持った状態で return するとよい。
fn get_element_by_id_as<T: JsCast>(id: &str) -> Option<T>
{
let w = window()?;
let d = w.document()?;
let e = d.get_element_by_id(id)?;
e.dyn_into::<T>().ok()
}
上記の関数は次のように使用できる:
let nyanko = get_element_by_id_as::<HtmlInputElement>("nyanko")?;
let w: Window = window()?;
w.alert_with_message( &nyanko.value() ).unwrap(); // 👈 '' が出力される
参考
- MDN
- web-sys
- https://docs.rs/web-sys/latest/web_sys/struct.Document.html#method.get_element_by_id
- https://docs.rs/web-sys/latest/web_sys/struct.Document.html#method.query_selector
- https://docs.rs/web-sys/latest/web_sys/fn.window.html
- https://docs.rs/web-sys/latest/web_sys/struct.Window.html
- https://docs.rs/web-sys/latest/web_sys/struct.Document.html
- https://docs.rs/web-sys/latest/web_sys/struct.Element.html
- https://docs.rs/web-sys/latest/web_sys/struct.HtmlInputElement.html
- wasm-bindgen