ぽんぽこ日記

プログラミング、読書、日々の生活

pyxelをRustから使ってみた。

Pythonでレトロゲームが作れるフレームワーク、pyxel

github.com

以前にもこのフレームワークを使って、2つほどサンプルプログラムを作ってみました。

ライフゲーム https://github.com/ponpoko1968/pyxel-life-game

疑似3Dダンジョン https://github.com/ponpoko1968/pyxel-dungeon

かねてより、Rustを覚えてみたいなと思っていた筆者は、最近pyxelのコア部分がRustで書きなおされたと知り、エンジン部分をRustから直接使えないかと調べてみました。作者のkitao氏が推奨・想定する使い方かどうかわかりませんが、pyxelの中核となるエンジン部分、pyxel-enginecrates.ioにも登録されており、pyxelのソースコードにはRustでかかれたユニットテストプログラムも書かれていたので、参考にして書いてみたいと思います。

今回は試しに、以前Pythonで書いたPyxelアプリ、ライフゲームを移植してみたいと思います。

以前のPython版のソースはこちら。

github.com

まずはプロジェクトを作成。

cargo new life_game_rusty_pyxel

Cargo.tomldependenciespyxel-engineを追加、pyxel-engineの最新バージョン(1.8.0)はcrates.ioに登録されていない*1ため、下記の記述を追加します。乱数を使って世界の初期状態を生成するため、randも使います。

[dependencies]
pyxel-engine = {git = "https://github.com/kitao/pyxel.git", branch = "main"}
rand = "^0.8.0"

ユニットテストプログラムの構造に順じて、Appというstructを作成、このstructにpyxel::PyxelCallbackで規定されているトレイト*2を実装します。(用語あってるのかな) 今回ライフが生息する世界はVecのVecで二次元配列で表現しました。

github.com

感想

筆者はここ数年、仕事ではSwift言語を用いてiOSアプリを開発しているので、普段Swiftを使って書いている立場からの感想です。

まず今回、Rustで書いた感想として、Swiftで得られた暗黙知はRustを書く上でも有効な点が多く、 SwiftプログラマがRustを習得しないのはもったいない、SwiftプログラマはRustを学ぶ上で、大きなアドバンテージがあると思いました。

習得が難しいと呼ばれるRustですが、もともとSwiftはRustに強い影響を受けて設計された言語と言われており、Rustから「所有権」の概念を引いたもの*3=Swiftという印象で、それ以外の「Swiftで言うところのOptional型」や、「型推論」「イミュータブル指向」などSwiftではおなじみの概念が多いので、他の多くの言語よりも入りやすいと思いました。 「SwiftプログラマのためのRust入門」と題して、「Swiftの○○はRustだとこう書く」みたいな1対1の対比で記事が書けるくらいこの2つの言語は近いなと思いました。

またSwiftと比べて、コンパイラの賢さは大幅に上回っているなと思いました。 Rustはエラーメッセージの親切さには定評がありますが、Swiftではクロージャ内に書かれた記述エラーがあったりすると最近のバージョンでも「よくわからんけどクロージャの中がおかしいからとにかく直して(意訳)」みたいなメッセージが出てへこむのですが、Rustコンパイラの、有名な所有権チェックなど、エラーの詳しさには驚かされます。旧来のコンパイラの概念を超えて、静的解析だけどがんばって実行時のシミュレートをしてみるよ的な意思を感じる仕組みです。

思えばSwiftでは数年前くらいまでは型推論の演算処理が重すぎて、対策として、型推論のメリットを捨てて出来るだけコードに型を明記することでビルドを高速化するというテクニックが横行するほどでした*4が、Rustの静的解析は随分先を行っている印象です*5

あと、他言語とのInteroperabilityですが、RustはC言語で書かれたレガシーなソフトウェア資産の利用も比較的容易であるのは周知かと思います。

今回Pyxelのコードを読んだところ、そればかりか、Pyxelの実装ではPythonとRustのバインディングにpyo3というライブラリが使われていて、Rustはこんなに簡単にPythonと結合できるのかという点も驚きでした。強力なマクロ機能がもたらす恩恵なのかと思いますが、この辺りはおいおい詳しくなりたいと思います。

参考にした本・情報源

概ね公式ドキュメントを読みつつ要所要所をググりながら進めました。

The Rust Programming Language - The Rust Programming Language

C/C++の知識がある人にはこれがおすすめです。Rust公式ドキュメントも実行時のコンピュータ内部のメモリのイメージを前提として説明されてますが、そういったコンセプトを進めた本ではないかと思います。

Pythonから入る人はこちらが良さそうです。

文法を確認するためのPlayground的なものはこちらを使いました。

replit.com

*1:2022年8月30日現在

*2:Swiftでいうところのprotocol

*3:SwiftはCocoaのrun-loop/autoreleasepoolの仕組みでメモリ管理されることが多いため、この仕組みは外されたのだと思います

*4:最近はそんなことはありません

*5:パフォーマンスについては大規模なコードを書いてみないとわかりませんが