minutus ブログ 下書き
このところ、夏休みの自由研究として mruby と Rust をいい感じにつなぎこむというのをやっていた
いちおう実用的といえるレベルまで来たのでリリースした
Minutus とは
Minutus は、Rust と mruby をいい感じに結合するライブラリ
できること
Rust の中で Ruby のスクリプトを eval する(Rust に mruby を埋め込む)
Rust で mrbgem を作る(mruby に Rust を埋め込む)
「Battery Included」を目指す
Cargo.toml に minutus = { version = "*", features = ["link_mruby"] } と書くだけで mruby が eval できる
minutus-mrbgem-template mruby-exampleとコマンドを打つだけで mrbgem のテンプレが生成される
mruby 連携の実装の詳細を可能な限り隠蔽する
mruby から値を取り出したり、mruby に値を渡したりを、mruby の C API を知らなくてもできるようにする
Minutus を作ったモチベーション
人類には「Rust の CLI ツールに mruby の DSL を入れたい」という普遍的な欲求がある、ということは広く知られている
しかし、実現には高いハードルがある
ハードル一覧
新しい mruby で動くバインディングがない
頑張って探したらあるかもしれないが、サッと見た感じでは無さそう
使いたくなった人が思い思いに fork して更新しているらしいという噂を聞いた
あと個人的には、mrusty のマクロの文法はRustの文法から乖離しすぎていると感じる
かといって、mruby の C API を bindgen 経由で触るのは茨の道
unsafe 祭り
mruby の C API に詳しくないと使えない
mruby の C API は関数ではなくマクロとして提供されてることがあるが、bindgen はマクロまで拾ってくれないので一筋縄でいかない
mrb_value を Rust の型に変換するのは超面倒
ボイラープレートが大量に必要
Rust で作ったバイナリを mrbgem にするのは地味にハマりどころが多い
特に src/ 以下に Cのソースコードが無いと(おそらく) _init 関数が呼ばれないところ
というわけで、最新の mruby で動き、かつ「いい感じに」mruby を触れるインターフェースは、人類が求めるものである
最新の mruby →
mruby 3.1.0 で動く
mruby と rust の間に一層挟んでいるため、多少 mruby 側の I/F が変わっても簡単に追従できる
いい感じ →
何も考えずに mruby と Rust で値をやり取りできる
Rust の世界で struct を定義し、それを mruby の世界でクラスとして利用できる
課題
GC と向き合えていない
利用中の値がうっかり GC されることは無いはず、かつ基本的には正しくメモリの開放を実装できていると思っている
しかし、メモリリークしてない自信がない
あと mruby_load_string の返り値に対する GC の挙動がよくわかってない(いつ free されるんだろう...)
余裕があったらこのあたりを検証したいが、どうやったら検証できるのかわかっておらず...
mruby のメソッドを安全に呼ぶ方法がない
現状では define_funcall! で定義した関数がエラーになったときパニックしてしまう
これはちょっとマクロを修正したらいい感じにできるはずなのでやっていきたい
キーワード引数のある mruby のメソッドを呼ぶ方法がない
Rust の関数にはキーワード引数がなく、define_funcall! に渡すシグネチャが Rust の文法からどうしても乖離してしまうため、マクロの文法をどうするか悩んでいる
マクロのエラーメッセージが雑すぎて文法ミスをデバッグできない
例えば define_funcall! { fn hoge(i64) -> i64 } はコンパイルが失敗するが、unexpected tokenみたいなメッセージしか出ないため、何が悪いのか全然わからない(引数名を書き忘れている)
もっとわかりやすいメッセージ・サジェスチョンを出せるようにしたい
Rust のモジュールシステムとうまく組み合わせられるのか未検証
define_funcall! や wrap と、それらを利用するコードが同じファイルにまとまってるパターンしか検証しておらず、それらが別モジュールに分離したときにうまく動くか怪しい
これもやるだけのタスクなので、そのうちやりたい
多分パフォーマンスがめっちゃ悪い
mruby と Rust の世界で値をやり取りするときの関数コールの回数がエグいし、inline 展開してない
何も考えず片っ端から inline にしていけばええという話かもしれない
wrap で生成したクラスに initialize_copy を実装してないので、たぶん dup すると死ぬ
やるだけなので暇なときにやる
実用的な例が存在しない
例えば Rust の資産を mruby にポートするというようなことをやって、minutus 自体の欠陥を洗い出して洗練させつつ、minutus 利用者が参考にできるようにしたい
例えば actix-web の mruby ラッパーを作りたいなと思っている
その他雑感
Rust の document 書きエクスペリエンスが良い。doc test が最高。何もしなくても動く。
これ Ruby にもほしい...
下回りに詳しくなれたような気がする
リンカの気持ちになれた
Rust に詳しくなった
型システム
crate 周りのエコシステム
docs.rs あたりは実際にやってみて初めて分かる制約がある(ビルド時にネットワークアクセスが封じられる等)
自分で Minutus を作ってアレだが直近で使う用途がないので、みんな使ってくれたら嬉しい
面白い使いみちのアイディアがあったら教えて下さい
こんなことできないの?とか、ドキュメントがおかしいとか分かりづらいとかも、指摘していただけると嬉しいです
まとめ
Minutus という mruby と Rust の連携をいい感じにやるライブラリを作った
課題はたくさんある
Rust と mruby に詳しくなった
みんな Minutus を使って面白いことしてほしい
Special Thanks
ruby-jp の mruby チャンネルの皆様には、アイディアを頂いたり、mrbgem のビルドシステムについて知見を頂いたり、大変お世話になりました。この場を借りて感謝いたします。
以上