2
1

More than 1 year has passed since last update.

RustなWASMでweb-sysのgetElementByIdやquerySelectorの結果得られるElementから適当なHtmlInputElement等へ参照する型を変換する方法

Last updated at Posted at 2022-08-18

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(); // 👈 '' が出力される

参考

License

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1