PyO3 で既存 Python 評価器を Rust core にする — wasm-bindgen でブラウザにも配るための設計
同じ判定ロジックを複数の言語で動かしたい。
素直にやれば各言語で書き直すことになりますが、実装が二つに分かれた時点から仕様は少しずつずれ始めます。今回はそれをどう一つに保つかという設計の話をします。
私たちは、店舗ごとに設定された条件を「式木」として評価する仕組みを Python で実装していました。サーバ内では問題なく動いていましたが、同じ評価をブラウザでも動かす必要が出たとき、JavaScript への書き直しは選びませんでした。評価ロジックの正本が二箇所に分かれるコストのほうが長期的には重いからです。数値の比較、真偽値への強制、NULL の扱い…。一つひとつは些細でも別々に育った実装はいつか必ず食い違います。
そこで、評価エンジンを Rust で一度だけ書く方式を取りました。Rust の core を PyO3 で Python 拡張として、ブラウザ向けには wasm-bindgen を用いて Wasm として配布する。実装を一つに寄せることでロジックそのものが分岐する余地を大きく削減できます。
トークの中心は PyO3 です。Python と Rust のあいだで値をどう変換するか、Rust の Result をどう Python の例外へ橋渡しするか、maturin での配布をどうするか等、実際に Rust core 化を進めるうえで生じる設計・実装上の論点や、つまずきやすいポイントを紹介できればと思います。
なお、本トークでは Rust や WebAssembly の予備知識は前提にしません。ただし、Rust のコード例は登場するので基本的な構文に触れたことがあると実装部分は追いやすくなります。
本発表を通じて、どこまで Python のままでよくて、どこから Rust + PyO3 に踏み込むのかの見極めを一緒に考えられたらうれしいです。
このトピックに関するあなた自身の経験を教えてください
Recustomer は eコマースの注文後体験を支援する SaaS を提供しており、私はそのプラットフォーム開発に従事しています。
店舗ごとに設定された条件をデータとして扱う仕組みが必要で、式木の IR を Python で設計し、評価器や JSON コーデック、conformance test を書いてきました。これをブラウザでも動かしたくなり、今は Rust core + PyO3 + wasm-bindgen での作り直しを進めているところです。
Python は約 5 年、Rust はまだ 1 年程度で手探りです。個人で作っている nblm-rs (https://github.com/K-dash/nblm-rs) でも Rust core を PyO3 + maturin で Python ライブラリにする構成を試していて、今回の発表と近いやり方を実際に触っています。
PyCon JP 2025 では Streamlit の商用 SaaS 化の話で登壇しました。
この発表を通じて、参加者とどんな議論が可能ですか?
現在、PyO3 を使っている人だったり、Python のコードを Rust + PyO3 に置き換えるかどうか、その線引きの基準等を話してみたいです。速くなるのは分かっていても Rust を読める人がチームにいないと保守がつらいとか、そのあたりの現場のリアルを交換できたらうれしいです。
もう一つは「同じロジックを複数の言語で動かしたい」という場面に出くわした人と話してみたいです。サーバはPython、フロントは JavaScript みたいに実行環境が分かれているときにロジックの正本をどこに置いてどう一本化するか等、みなさんがどう折り合いをつけているのか意見交換してみたいです。
Recustomer 株式会社の Platform Team に所属し、主に認証・認可等の共通モジュール化、デプロイメントの自動化、開発環境の標準化などの抽象基盤の開発に従事しています。