Rustで作ったWasmでデータを永続化する話
自分はまだWasmもRustも初心者なので、Github Copilotに相談をしながら作っているので、間違っている、こうしたほうが良いというアドバイスがあればお願いします。ひとまず自分の理解のアウトプットです。
→今回作ったwasmモジュール[https://www.npmjs.com/package/wasm-ziparchive]
データを永続化したかったわけ
Wasmは基本的にステートレスで状態を保持することがありません。しかし、ウェブ上の処理の一部分を代替してもらうためにも、状態を一時的に保管しておきたいということはあります。今回は、フロントエンドのデータfetchとZipファイルにまとめるためのデータ送信を非同期にしなければボトルネックが多くてやってられなかったためです。
永続化したオブジェクトの生成
wasmは先の通りステートレスであるため、基本的にはデータは保持をしません。また、Rustも基本的には所有権を持つ変数がスコープを抜けるときに開放されます。そこでオブジェクトをヒープに配置し、スコープを抜けても保持をし続けることですることで、メモリからの状態を保持するためにはメモリの管理をする必要があります。
Wasmとjavascript間で操作をしたいメモリの情報をやり取りする必要があります。永続したいオブジェクトをBoxで囲んでヒープに配置。そのメモリのポインタをJavascriptに返却します
#[wasm_bindgen]
pub fn create_object() -> JsValue{
let persistence_object = object::new();
let boxed_object = Box::new(persistence_object);
let boxed_object_ptr = Box::into_row(boxed_zip);
JsValue::from(*boxed_zip_par as u32)
}
- Javascriptにはメモリの値(数値)が返却されます。
オブジェクトの操作時
javascriptがわからの呼び出しでオブジェクトを操作するときは、オブジェクトを作成した際に返却したメモリの値を使って、ヒープからオブジェクトを呼び出し、操作をします。
#[wasm_bindgen]
pub fn operation(ptr: JsValue, operation_key: &str) -> Result<(), JsValue>{
let object_memory = ptr.as_f64().unwrap() as usize as *mut object;
let mut obj = unsafe{ Box::from_raw(object_memory)};
// 何かしらの操作
let _ Box::into_raw(obj);
Ok(())
}
メモリのポインタからオブジェクトを復元して、操作をします。
注意しなければならないのが、ポインタからオブジェクトを復元しているので、再度ポインタに戻さないと、所有権を持つオブジェクトが関数のスコープから外れると解放されてしまいます。再度ポインタに変換をするとアクセスすることが可能です。
逆に、この永続化しているメモリを解放しようと思ったら、メモリを戻さずにスコープを抜けると解放されることとなります。
おわり
Rustはメモリが所有権の形で管理をされており、基本的にはスコープを抜けるとメモリは解放され、メモリの安全性が高い言語です。ヒープで配置をするまではなんとなく理解はできるのですが、生ポインタを作ってJavascriptと共有し、操作をするときにはその所有権を再度取得して復元して…また生ポインタにして返すといったことをするとは思ってもみなかったです。
Rust全然わかんない。でも、わかんないから面白い。
Discussion