大規模なRustプロジェクトを効果的に整理する方法
Wenhao Wang
Dev Intern · Leapcell

Rustプロジェクトの構造
多くの学習者はRustを学習する際、自分のプロジェクトのファイル構造が正しいのか、標準的なのか疑問に思うことがあります。この記事では、大規模なRustプロジェクトがどのようにコードを整理しているのか、最も基本的なmain.rs
とlib.rs
から探求していきます。
クレート
- クレートはRustの基本的なコンパイル単位です。各クレートは独立したコンパイルターゲットであり、ライブラリ(libクレート)または実行可能ファイル(binaryクレート)のいずれかになります。
- クレートにはルートファイルがあります。ライブラリクレートの場合、
src/lib.rs
です。バイナリクレートの場合、src/main.rs
です。
パッケージ
しかし、基本的なRustプロジェクトはこれらの2つのファイルだけで構成することはできません。
パッケージは、1つまたは複数のクレートの集合です。パッケージには、パッケージのメタデータと依存関係を定義するCargo.toml
ファイルとCargo.lock
ファイルが含まれています。
実際のプロジェクトでは、クレートはコードとモジュールのみを含み、Cargo.toml
ファイルとCargo.lock
ファイルはパッケージの一部であり、パッケージ全体の管理とビルドを担当します。
たとえば、cargo new sdk
を使用してライブラリを作成する場合、結果の構造は次のようになります。
ファイル構造の例
// ライブラリクレート
sdk/
├── Cargo.toml
├── Cargo.lock
└── src
└── lib.rs
or
// バイナリクレート
sdk/
├── Cargo.toml
├── Cargo.lock
└── src
└── main.rs
TOMLファイル
TOML
ファイルは、依存関係とバージョン情報を管理するために使用されます。例:
[package] name = "sdk" version = "0.1.0" edition = "2021" # その他のキーとその定義については、https://doc.rust-lang.org/cargo/reference/manifest.html を参照してください [dependencies]
エラー処理を簡素化するために、一般的に使用されるパッケージthiserror
を追加します。cargo add thiserror
コマンドを使用できます。
[package] name = "sdk" version = "0.1.0" edition = "2021" # その他のキーとその定義については、https://doc.rust-lang.org/cargo/reference/manifest.html を参照してください [dependencies] thiserror = "1.0.61"
テストコードとパフォーマンステスト
現時点で、私たちはすでに完全なプロジェクトを作成しています。しかし、あらゆるプロジェクトの重要な部分はテストであり、これにはユニットテストとパフォーマンステストが含まれます。では、これらのテストファイルはどこに配置すべきでしょうか?引き続きsdk
プロジェクトを例として使用します。
コミュニティと公式の標準に従い、テストファイルとベンチマークファイルは、以下に示すように、src
と同じレベルのtests
ディレクトリとbenches
ディレクトリに配置する必要があります。
sdk/
├── Cargo.toml
├── src/
│ └── lib.rs
├── tests/
│ ├── some-integration-tests.rs
│ └── multi-file-test/
│ ├── main.rs
│ └── test_module.rs
└── benches/
├── large-input.rs
└── multi-file-bench/
├── main.rs
└── bench_module.rs
プロジェクトの初期段階では、ユニットテストは関連するコードファイルの真下に直接配置できるため、multi-file-test
ディレクトリとファイルを作成する必要はありません。ただし、開発が進み、テストコードがかなりのスペースを占有し始めたら、メインコードを整理するためにtests
フォルダーに移動することをお勧めします。
tests/
には、主に機能の実装を検証するための機能テストコードが含まれています。benches/
には、主にパフォーマンス(例:サービスAPIのパフォーマンステスト)を測定するためのパフォーマンステストコードが含まれています。
ワークスペース
大規模なプロジェクトが複数のRustプロジェクトで構成されている場合はどうでしょうか?たとえば、sdk
クレートが1人の開発者によってメンテナンスされており、それに基づいてCLIプロジェクトとサーバープロジェクトも構築する必要があるとします。コードを3つの別々のプロジェクトに分割する必要がありますか?それは面倒になる可能性があります。それらをまとめて管理する方法はありますか?はい、ワークスペースを使用します。
Rustでは、ワークスペースは、単一のプロジェクト内で複数のパッケージを整理および管理する方法です。ワークスペースは、複数の関連パッケージにわたる依存関係の管理、ビルド、およびテストを簡素化するツールとメカニズムを提供します。
ワークスペースを使用する利点
- 複数のパッケージを整理する:ワークスペースを使用すると、ライブラリクレート、CLIツール、またはその他の種類のパッケージを含む複数のパッケージをグループ化できます。
- 共有依存関係:ワークスペース内のすべてのパッケージは、単一の
Cargo.lock
ファイルを共有し、一貫した依存関係のバージョンを確保し、競合を回避します。 - ビルドプロセスの簡素化:ルートワークスペースディレクトリで
cargo build
またはcargo test
を実行すると、すべてのワークスペースパッケージが再帰的にビルドおよびテストされます。 - 一貫性:共有の
Cargo.lock
ファイルと統一されたビルドコマンドにより、すべてのパッケージが一貫性を保ち、適切に連携されることが保証されます。
ワークスペースの構造
sdk
、cli
、およびserver
を同じワークスペース内に配置するとします。ディレクトリ構造は次のようになります。
一般的なワークスペースは、ルートのCargo.toml
ファイルと、独自のCargo.toml
ファイルとソースディレクトリを持つ複数のサブパッケージを含むトップレベルディレクトリで構成されています。
my_workspace /
├── Cargo.lock
├── Cargo.toml
├── crates/
│ ├── sdk/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └──── tests/
│ │ ├── some-integration-tests.rs
│ │ └── multi-file-test/
│ │ ├── main.rs
│ │ └── test_module.rs
│ ├── cli/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ ├── bin/
│ │ │ ├── named-executable.rs
│ │ │ ├── another-executable.rs
│ │ │ └── multi-file-executable/
│ │ │ ├── main.rs
│ │ │ └── some_module.rs
│ │ └──── tests/
│ │ ├── some-integration-tests.rs
│ │ └── multi-file-test/
│ │ ├── main.rs
│ │ └── test_module.rs
│ └── server/
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ ├── bin/
│ │ ├── named-executable.rs
│ │ ├── another-executable.rs
│ │ └── multi-file-executable/
│ │ ├── main.rs
│ │ └── some_module.rs
│ ├── tests/
│ │ ├── some-integration-tests.rs
│ │ └── multi-file-test/
│ │ ├── main.rs
│ │ └── test_module.rs
│ └── benches/
│ ├── large-input.rs
│ └── multi-file-bench/
│ ├── main.rs
│ └── bench_module.rs
ワークスペースTOMLファイル
より効率的なCargoの第2世代リゾルバーを使用するために、リゾルバーresolver = "2"
を指定します。
[workspace] resolver = "2"
ワークスペースパッケージ情報を定義します。
[workspace.package] name = "my-workspace" version = "0.1.0" edition = "2021"
ワークスペースメンバーを追加します。
[workspace] members = [ "crates/sdk", "crates/cli", "crates/server", ]
ワークスペースメンバー間で共有される依存関係を指定します。
[workspace.dependencies] thiserror = "1.0.61"
「しかし、それはすでにsdk
のCargo.toml
で定義されているはずです。なぜワークスペースでそれを再度定義するのですか?」と疑問に思うかもしれません。
これは、ワークスペースが依存関係の管理を集中化できるためです。cli
とserver
の両方がthiserror
も必要とする場合、それぞれCargo.toml
ファイルにthiserror = "1.0.61"
を個別に定義する必要がありますか?そうすることもできますが、これにより潜在的な問題が発生します。プロジェクト間で異なるバージョンが使用されている場合、コンパイルが遅くなり、コンパイルされたバイナリにthiserror
の冗長なコピーが含まれている可能性があります。
コンパイル時間とバイナリサイズを最適化するために、ワークスペースで統一された依存関係のバージョンを設定します。
# ワークスペースの`Cargo.toml` [workspace.dependencies] thiserror = "1.0.61"
# `cli`および`server`パッケージ内 [dependencies] thiserror.workspace = true
パッケージ間の依存関係
cli
とserver
がsdk
のメソッドを使用できるようにするには、ワークスペースで依存関係を宣言する必要があります。
[workspace.dependencies] sdk = { path = "crates/sdk" } cli = { path = "crates/cli" } server = { path = "crates/server" }
次に、パッケージ固有のCargo.toml
ファイルで:
[dependencies] sdk.workspace = true thiserror.workspace = true
これにより、ワークスペース内のすべてのプロジェクトが、依存関係のバージョンを複製することなくsdk
を参照できるようになります。
Leapcellをご紹介します。Rustプロジェクトをホストするのに最適な選択肢です。
Leapcellは、Webホスティング、非同期タスク、およびRedisのための次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、またはRustで開発します。
無制限のプロジェクトを無料でデプロイ
- 使用量のみを支払います - リクエストも料金もかかりません。
比類のないコスト効率
- アイドル料金なしの従量課金制。
- 例:25ドルで平均応答時間60msで694万リクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOpsの統合。
- 実行可能な洞察のためのリアルタイムのメトリックとロギング。
簡単なスケーラビリティと高性能
- 高い同時実行性を簡単に処理するための自動スケーリング。
- 運用のオーバーヘッドはゼロ - 構築に集中するだけです。
ドキュメントで詳細をご覧ください!
Xでフォローしてください:@LeapcellHQ