Rustの設計と実装Tipsを学ぶ

Swayに学ぶ実用Rustパターン Part 1: モジュラーモノレポと拡張可能なCLI設計

解析日: 2026/8/28
対象コミット: dad95cc
リポジトリ: FuelLabs/sway
RustCLIMonorepoWorkspaceArchitecture

1. 概要

Rustは、パフォーマンスと信頼性を両立するシステムプログラミング言語として、WebAssembly、オペレーティングシステム、さらにはブロックチェーンといった多岐にわたる分野で採用されています。

本記事シリーズでは、Fuelブロックチェーン向けのスマートコントラクト言語Swayのツールチェイン (forc) を分析し、大規模なRustプロジェクトで役立つ実践的な設計パターンと実装テクニックを学びます。

Part 1となる今回は、特に「モジュラーモノレポアーキテクチャ」と「拡張可能なコマンドラインインターフェース (CLI) の設計」に焦点を当てます。SwayプロジェクトがどのようにRustのワークスペースを活用し、forcツールがどのようにしてそのエコシステムの中心となり、さらにプラグインによって拡張されているのかを見ていきましょう。

2. アーキテクチャ

Swayプロジェクトは、Rustのワークスペース機能を用いたモジュラーモノレポとして構築されています。これにより、関連する複数のクレート(ライブラリやバイナリ)を単一のリポジトリで管理し、依存関係の調整や一貫したバージョニングを容易にしています。

プロジェクトの中核をなすのは、Sway言語のコンパイラ群 (sway-core, sway-ast, sway-irなど) と、その開発ツールチェインであるforcです。

forcはRustのCargoに相当する役割を果たし、Swayプロジェクトのビルド、テスト、デプロイなど、開発ワークフロー全体をオーケストレーションします。その機能は、forc-pkg (パッケージ管理)、forc-test (テスト実行) といったコアクレートに加え、forc-plugins/*ディレクトリに存在する多様なプラグイン (forc-doc, forc-fmt, forc-lspなど) によって拡張されています。

forcツールチェインの構造

forcは、ユーザーからの入力を受け付け、内部のパッケージ管理クレートや外部プラグインと連携することで、Sway開発者に統合された体験を提供します。

graph TD A[User] --> B["forc (CLI Main)"]; B --> C["forc-pkg (Package Management)"]; B --> D["forc-doc (Plugin)"]; B --> E["forc-fmt (Plugin)"]; B --> F["forc-lsp (Plugin)"]; B --> G["Other forc-plugins"]; C --> H["Forc.toml / Cargo.toml (Configuration)"]; H --> I["Dependency Resolution & Build"];

この図からわかるように、forcは単一の実行可能ファイルとして機能しながら、その実態は複数のクレートとプラグインによって構成される、高度にモジュール化されたシステムです。これにより、各機能が独立して開発・保守されつつ、統一されたユーザーインターフェースを提供しています。

3. この記事で学べること

本記事を通じて、読者の皆さんは以下の実践的な知識とパターンを習得できます。

  1. Rustワークスペースを活用した大規模プロジェクト管理のノウハウ
  2. forcツールチェインに学ぶ拡張可能なCLIツールの設計思想
  3. Facadeパターンを用いたAPIの単純化テクニック
  4. 設定ファイルをデータとして扱う堅牢なアプローチ
  5. プロジェクトの安定性を高める依存関係の厳格なバージョン管理

4. 実践的な実装・コード解説

4.1. Rustワークスペースによるモジュラーモノレポ

Swayプロジェクトは、リポジトリルートのCargo.tomlでワークスペースを定義しています。これは、複数のクレートを一つのリポジトリで管理する標準的な方法です。

# リポジトリルートのCargo.toml (抜粋)
[workspace]
resolver = "2"
members = [
    "forc",
    "forc-pkg",
    "forc-test",
    "forc-util",
    "forc-plugins/*", # ワイルドカードでプラグインクレートを検出
    "sway-ast",
    "sway-core",
    # ... その他多数のクレート
]

[workspace.package]
authors = ["Fuel Labs <fuel@fuel.sh>"]
license = "Apache-2.0"
repository = "https://github.com/FuelLabs/sway"
# ... 共通のパッケージメタデータ

[workspace.dependencies]
tokio = { version = "1.12", features = ["macros", "rt-multi-thread"] }
# ... 共通の依存関係

membersフィールドはワークスペースに含まれるクレートのパスを指定します。forc-plugins/*のようにワイルドカードを使用することで、新しいプラグインの追加が容易になります。また、[workspace.package]や[workspace.dependencies]セクションでは、ワークスペース内の全クレートに適用される共通の設定や依存関係を定義でき、一貫性と管理のしやすさを向上させています。

4.2. forc-pkgに学ぶFacadeパターン

forc-pkgクレートは、Swayプロジェクトのパッケージ管理を担う中心的な役割を果たします。そのlib.rsは、内部の複雑なモジュール構造を抽象化し、主要な型をトップレベルで再エクスポートするFacadeパターンを採用しています。

// forc-pkg/src/lib.rs (抜粋)
pub mod lock;
pub mod manifest;
pub mod pkg;
pub mod source;

pub use lock::Lock;
pub use manifest::{BuildProfile, Manifest, PackageManifest, PackageManifestFile};
pub use pkg::{BuiltPackage, Dependency, LocalManifest, Pin,
               Pinned, PinnedDep, PinnedManifest, Pkg, Rev}; // PinnedManifest は pkg::PinnedManifest の間違いか?
pub use source::{DependencySource, Source}; 
// ... その他、内部モジュールから必要なものを再エクスポート

このようにpub useを多用することで、forc_pkg::Lockやforc_pkg::PackageManifestのように、クレートの利用者は内部モジュールの詳細を知らずとも、必要な型にアクセスできます。これは、ライブラリのAPIをシンプルかつ使いやすくするための強力な手法です。

4.3. 内部スクリプトによる依存関係の厳格な管理

Swayプロジェクトには、check-dep-versionsというRust製のスクリプトが存在します。これは、Cargo.tomlで定義されている依存関係のバージョンが厳格なルール (x.y.z形式またはx.y形式) に従っているか、また許可された依存関係のリストに含まれているかを自動でチェックします。

// scripts/check-dep-versions/src/main.rs (抜粋)
use std::collections::HashSet;

// ... 省略

// 許可された依存関係の名前のHashSet
let allowed_xyz_set: HashSet<String> = ALLOWED_XYZ_DEPS.iter().map(|s| s.to_string()).collect();

// ... 省略

// 依存関係が許可リストに含まれているか、バージョン形式が正しいかをチェック
fn check_pkg(
    dep: &Dependency,
    allowed_xyz_set: &HashSet<String>,
    allowed_xy_set: &HashSet<String>,
) -> Result<(), Error> {
    // ... バージョン形式と許可リストのチェックロジック
}

// ... 省略

HashSet<String>を使用して、許可された依存関係名を効率的にルックアップしている点に注目です。これは、ビルドの安定性を高め、予期せぬ依存関係の更新による問題を未然に防ぐための、実用的な品質保証の取り組みです。CI/CDパイプラインに組み込むことで、プロジェクト全体の整合性を維持できます。

5. 実務に持ち帰れるTips

Tip 1: Rustワークスペースを最大限に活用したモノレポ設計

Tip 2: 拡張可能なCLIアーキテクチャの構築

Tip 3: FacadeパターンによるライブラリAPIの単純化

Tip 4: 設定ファイルをデータとして扱う堅牢なパターン

Tip 5: CI/CDと連携した依存関係の厳格なバージョン管理

6. トレードオフと注意点

6.1. モノレポの複雑性とビルド時間

モノレポは、依存関係管理を単純化し、開発の一貫性を保つ一方で、プロジェクトが大規模になるにつれてビルド時間が長くなる傾向があります。Swayプロジェクトでは、justコマンド (just build-all) を用いて効率的なビルドを試みていますが、適切に設定しないと開発体験を損なう可能性があります。

6.2. プラグインシステムの管理コスト

拡張可能なCLIは非常に強力ですが、コアツールとプラグイン間のバージョン互換性維持、APIの安定性保証、ユーザーへの適切なドキュメント提供など、管理コストが発生します。プラグイン開発者とユーザー双方にとって使いやすいエコシステムを構築するための継続的な努力が必要です。

6.3. 厳格な依存関係バージョニングのオーバーヘッド

check-dep-versionsスクリプトによる厳格なバージョン管理は、ビルドの安定性向上に寄与しますが、新しい依存関係の追加や既存の依存関係のバージョンアップには、その都度許可リストの更新が必要となります。これは開発者にとって追加のオーバーヘッドとなる可能性があり、安定性と柔軟性の間のバランスを考慮する必要があります。

7. まとめ

Swayプロジェクトの分析を通じて、私たちはRustを用いた大規模システム開発におけるいくつかの重要なパターンを学びました。

これらのパターンは、Swayのようなブロックチェーン関連のツールチェインに限らず、あらゆる大規模なRustプロジェクトに応用可能です。ぜひ皆さんのプロジェクトにこれらの知見を持ち帰り、より堅牢で保守しやすいシステム開発に役立ててください。

次回Part 2では、Swayコンパイラの内部に焦点を当て、コンパイラ設計におけるRustの活用方法、特にAST (抽象構文木) の管理や、Visitorパターンの利用などについて深く掘り下げていきます。