1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust で WASM(WebAssembly)モジュールを作ってみた

Posted at

Rust について

Rust は、Stack Overflow で7年連続「最も愛されている言語ランキング」1位を獲得している
、人気のある言語です。型安全でありながら、C言語並みのパフォーマンスを発揮することができるため、特に低レベルでの処理を必要とするゲームやWebアプリケーションでの利用が増えているようです。
今回は、以前から興味があった Rust で WebAssembly モジュールを作成し、HTML上にアニメーションを描画してみました。

Rust は「最高難易度」と謳われることもあり、Python などと比べると異なる点が多いです。
クラスの概念がないことは、オブジェクト指向の言語を主に扱ってきた自分にとってかなり新鮮でした。
所有権や借用、ライフタイムなどの概念があるため、メモリ管理が非常に厳格で、このあたりは特に刺激となりました。

IDEは、JetBrainsの RustRover を使っています。

WebAssembly テンプレートの準備

Rust で WebAssembly を利用するためには、まず WebAssembly のプロジェクトテンプレートを準備します。今回は rust-webpack を使ってプロジェクトをセットアップしました。

プロジェクト用のディレクトリを作成し、以下のコマンドを実行して、Rust と WebAssembly のプロジェクトテンプレートを作成します。

npm init rust-webpack
npm install

アニメーションの描画

当初は 3Dオブジェクトのレンダリングを目指していましたが、JavaScript と Rust の連携が思った以上に大変だったので、一旦 2D でアニメーションを表示することにしました。
Rust でJS/TSと同様なレンダリングの記述をする必要があり、3D を扱うなら WebGL(Three.jsなど)で描画する方がはるかに楽かもしれません..

とはいえ、Rust は処理パフォーマンスが非常に高いため、リッチなコンテンツを作る際には大きな利点となるはずです。ゆくゆくは OpenGL などの技術と組み合わせて、より複雑なグラフィック処理にも挑戦してみたいと思っています。

今回の目的はあくまで Rust を触ってみる、ということにしておきます。

ひとまず完成した2Dアニメーション

cap.gif

大変だったこと

今回は、1枚の画像を範囲指定して表示するスプライト方式を使っています。
昔のゲームの手法ですね。

使用したスプライトは Game Art 2D からダウンロードできるフリー素材です。

やること自体は JavaScript と同じように、画像を読み込み、フレームごとに画像を切り替えてアニメーションを行うのですが、それに借用や変数のライフサイクルの制約がついた感じになります

クレートの使い方が独特

Cargo.toml に以下の感じで利用する関数を features に個別で追記しないといけない
そのうえで、モジュール内でも use を使ってインポートが必要

web-sys の場合

[dependencies.web-sys]
version = "0.3.22"
features = ["console", "Window", "Document", "Element", "HtmlCanvasElement", "HtmlElement",
    "CanvasRenderingContext2d", "Response", "HtmlImageElement"]

メモリ管理

スコープ抜けたらアクセスできなかったり、forget() しないと動かなかったり。
Web上に画像を表示して動かすだけでもかなり大変。
JSを扱わない処理であればもう少しシンプルな気がする

unwrap()

返り値が None になる可能性がある場合必要
忘れるとコンパイルできない

まとめ

Rust でWebAssembly のモジュールを作ってみましたが、思ったより難しかった。
というか Rust でJSを操作するのがちょっとしっくりこないというか...
ただ学びは多かったです。Rust は複雑だけど楽しい。もっと純粋に Rust を深く知りたいと思いました。

次の機会には、3Dレンダリングできるモジュールか、高パフォーマンスが必要とされる処理をこなすモジュールを作りたいです。

Rust 覚書

トレイト

メソッドを定義するもので、プロパティやデータを持たない。

クロージャー

無名関数のこと。

Box

ヒープ領域に保存される、サイズが不明な変数。

Vector

長さが不明な配列。
let mut v1 = vec![1, 2, 3];
のように使用する。

struct, trait, impl

struct はデータ構造を、trait は振る舞いを定義し、impl でデータ構造に振る舞いを定義する。これらを組み合わせてクラスのように扱える。

シャドーイング

同じ変数名で再定義することで、元の変数を隠す。ローカルスコープ内でのみ上書きしたような状態になる

参照(ref)

ポインタを取得する。
*pointer = 5 のようにポインタから値をバインドできる。
* は参照外しで使用する。

& との違い

match 文でパターンマッチする時に使い方が異なる。

Option型

Some, None を持ち、unwrap() とセットで使用する。

デバッグ用のプリント

:p ポインタ
:? プリミティブでない値の参照
:#? 整形された表示ができる。
len() でバイト数を取得。

構造体を表示する場合

#[derive(Debug, Clone)]
struct Point {
    x: i32,
    y: i32,
}

文字列

String::from("hello") で可変な文字列としてヒープに格納される。
不可変な文字列は static 領域に、可変にするには mut を付けてヒープに格納される。

スタックとヒープ

プリミティブ型でない場合、スタックには値ではなくポインタが保持される。8byte のポインタ、8byte の長さ(文字列なら文字数、配列ならサイズ)、8byte の容量情報が含まれる。

所有権が移動しない型

整数型(i32, u32など)、浮動小数点型(f32, f64)、ブーリアン型(bool)、文字型(char)、参照型(&T)はスタックメモリを使用し、シャローコピーで所有権が移動しない。

所有権が移動する型

String や Vector、Box などはヒープに実データを保存し、所有権システムが適用される。

return の省略

Rust では return を明示的に書かずに、セミコロンを付けないことで返り値とする。

可変参照

let s2 = &mut s1 のように可変な参照を作ると、そのライフタイムが終了するまで元の変数にはアクセスできない。
不変な参照は複数作成可能。

ダングリングポインタ

スコープ外の参照は作らないようにする必要がある。

引用符(” と ’)

シングルクォート(’)は1文字用で、4byte メモリを使用する。

Generic Lifetime Annotation

'a はトレイトの引数のライフタイムを指定し、短い方に合わせてダングリングポインタの発生を防ぐ。

Self と self

Self は struct の型自体を指し、コンストラクタなどで使用される。self はインスタンスを指す。

1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?