#9512 - Rewrite It in Rust
- Fish シェルが Rust で再実装された。C++ コードは完全になくなり、ほぼ 100% 純粋な Rust で構成されている
- 約2年前、Fish を C++ から Rust に移行する PR(#9512)を開始した
- Fish は過去にも C から C++ へ移行した経験があるが、Rust への移行ははるかに大規模なプロジェクトだった
C++での問題点
- ツールとコンパイラの違い: C++ のツールは使い勝手がよくなく、最新の C++ 標準を採用するとパッケージ作成者やコントリビューターにとって複雑さが増す
- スレッド安全性: Fish の内部コマンド実行は現在シリアルに行われており、非同期プロンプトやノンブロッキング補完機能を追加するには並列処理が必要
- 言語の複雑さ: C++ のヘッダーファイル、テンプレート、文字列処理などは複雑で安全ではない
- コミュニティ: C++ は多くのコントリビューターを引きつけられない
- 依存関係の問題: 特定の C ライブラリ(curses)の不安定さやビルド問題によって手間がかかっていた
Rustを選んだ理由
- 楽しさと面白さ: Fish は趣味プロジェクトであり、楽しくて興味深い言語が必要だった。Rust はコントリビューターにとってより魅力的
- 優れたツール:
rustup でコンパイラを簡単にインストールでき、エラーメッセージも明確
- エルゴノミクス: 明示的な
use システムと、Option や Result のような安全な機能を提供
- 優れた言語設計: Rust のポインタとオプションのシステムは C++ よりはるかに安全
- 並列処理のサポート: Rust の Send と Sync により安全な並列処理が可能
- 依存関係管理: YAML、JSON など外部形式のサポートを簡単に追加できる
プラットフォーム対応
- macOS、Linux、BSD など主要なプラットフォームの大半をサポートし、Windows のネイティブ対応は目標としていない
- Fish は UNIX 中心のシェルであり、Windows 環境よりも UNIX API とスクリプト言語に重点を置いている
移植プロセス
- Fish は「テセウスの魚」という方式で C++ から Rust へ段階的に移行した。コンポーネントを1つずつ Rust に移し、C++ と Rust が共存できるようにした
- テセウスの船(Ship of Theseus): 「1隻の船の木の板をすべて新しいものに交換したら、それはなお同じ船なのか?」
- FFI の活用: autocxx を使って C++ と Rust 間のバインディングを生成し、一度に1つのコンポーネントずつ移植した
- 大規模な移植: 特定の部分(例: I/O 処理など)は単独で移行し、複雑な FFI コードを減らした
- ツールの改善: 移植の過程で Rust と C++ の相互運用性の問題を解決するため、
autocxx をカスタマイズした
タイムライン
- 2023年1月: 初期 PR を開始
- 2024年1月: C++ コードを完全に削除
- 2024年12月: Fish 4.0 ベータ版をリリース
Rustとの摩擦
- 移植性の問題: Rust の
#[cfg(...)] アプローチは、低レベルでシステム差異を扱うには非効率
- ローカライズ: Rust のフォーマット文字列はコンパイル時に検証されるが、翻訳できない
- ビルド時間: LTO とデフォルトのリリースビルドの使用により、ビルド時間が長くなることがある
- 移植の過程でいくつかミスもあったが、ほとんどは簡単に解決できた
主な成果
- curses の削除: terminfo データベースを Rust crate に置き換え、グローバル状態とビルド問題を解決
- 単一実行ファイル: すべての依存関係を含んだ Fish バイナリを生成可能
- Fish パッケージを自己完結型でインストール可能にし、ユーザーが簡単に利用できるようにした
- 性能改善: メモリ使用量を最適化し、新機能の追加も容易になった
制限事項
- CMake を完全には排除できなかった
- Cygwin のサポートを終了: Rust のターゲットが存在しないため
- Windows では依然として WSL 経由でのみ実行可能
現在と今後
- Fish 4.0 は移植に成功し、性能も向上した
- Fish は引き続き UNIX シェルであり、Rust への移行によって新機能を追加できるようになった
- これで完全に Rust 化されたコードベースを持つことになり、従来より保守や機能追加が容易になった。Rust の利点を生かして新機能を追加できる
- 今回の移行は成功裏に完了し、コントリビューターとユーザーの双方に良い影響をもたらした
3件のコメント
fishの使い勝手がうらやましいのですが、互換性や性能などの問題のため、zshをできるだけfishに近くなるよう設定して使っています。変わったfishがどうなっているのか楽しみですね 👀
親しみやすいインタラクティブシェル - Fish
Hacker Newsのコメント
Fishチームに祝意を表するとともに、プロジェクトの詳細が興味深い。C++からRustへ完全移行した最大のプロジェクトなのか気になる。他のプロジェクトにとって有益な教訓になり得る
Rustに関する主な不満はバージョン検出サポートである。機能検出のほうが、ディストリビューション、Webブラウザ、コンパイラではより適している
移植の目標の一つはCMakeの排除だったが、失敗した。Cargoはビルドには優れているが、インストールについては単純すぎる。Fishには多くのスクリプトとドキュメントがあり、Cargoのユースケースには合っていない
数年前にbashからzshへ切り替えたときは満足していたが、新しいコンピューターでfishを使ってみると、zshが煩雑で時代遅れに感じられた。fishを数週間使ってみることを勧める
Cygwinをサポートしていないのは残念だ。RustがCygwinをビルドターゲットとしてサポートしてくれることを望む
Fishチームの努力に感嘆しており、プロジェクトが今後どう発展していくのか楽しみだ
ディストリビューションのパッケージャーが、Rust版fishをDebianのガイドラインに従ってどれほど容易にパッケージ化できるのか気になる
Fishチームに祝意を表するとともに、最高のシェルがさらに良くなったという意見だ。プロジェクトのタグラインを "Finally, a shell for the 00s!" に更新してはどうかと提案する
zshからFishに切り替えてから設定が簡単になり、Fishは期待どおりに動作するので、再び変更するつもりはない
cfg!マクロはtrue/falseにコンパイルされるため、ifガード内のコードはコンパイルされなければならない。my_featureなしでコンパイルすると失敗する可能性があるFishは自動補完と構文ハイライトのためにスレッドを使用しており、言語に並行性を追加する長期プロジェクトがある