課題2: Cargo実践とプロジェクト構築
マンダトリー要件
問題1:環境構築の確認(10点)
以下のコマンドを実行し、出力をキャプチャして提出しなさい。
# 1. バージョン確認(各2点)
rustc --version
cargo --version
rustup --version
# 2. インストール済みターゲットの確認(2点)
rustup target list --installed
# 3. インストール済みコンポーネントの確認(2点)
rustup component list --installed
提出物:
environment.txt: 上記コマンドの出力結果- プロジェクトの作成(5点)
問題2:Hello Worldプロジェクト(15点)
cargo new hello_rust
- main.rsの修正(5点)
// 期待される動作
// $ cargo run
// Hello, World!
// $ cargo run -- Alice
// Hello, Alice!
- ビルドと実行(5点)
提出物:
src/main.rs: 修正したソースコードbuild_comparison.txt: ビルド結果の比較(サイズ、コンパイル時間)- プロジェクト作成(5点)
問題3:ライブラリプロジェクト(25点)
calculatorという名前のライブラリクレートを作成しなさい。
cargo new --lib calculator
- 基本機能の実装(10点)
src/lib.rs:
/// 2つの数値を加算します
pub fn add(a: i32, b: i32) -> i32 {
// TODO: 実装
}
/// 2つの数値を減算します
pub fn subtract(a: i32, b: i32) -> i32 {
// TODO: 実装
}
/// 2つの数値を乗算します
pub fn multiply(a: i32, b: i32) -> i32 {
// TODO: 実装
}
/// 2つの数値を除算します(0除算はエラー)
pub fn divide(a: i32, b: i32) -> Result<i32, String> {
// TODO: 実装
// ヒント:b == 0の場合はErr("Division by zero")を返す
}
- テストの実装(10点)
各関数に対して最低2つのテストケースを作成:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
assert_eq!(add(-1, 1), 0);
}
#[test]
fn test_divide() {
assert_eq!(divide(10, 2), Ok(5));
assert!(divide(10, 0).is_err());
}
// TODO: subtract, multiplyのテストも追加
}
提出物:
calculator/src/lib.rs: 実装とテストtest_results.txt:cargo testの実行結果- プロジェクト作成と依存関係追加(5点)
問題4:依存関係の管理(20点)
json_readerプロジェクトを作成し、JSONファイルを読み込む機能を実装しなさい。
cargo new json_reader
cd json_reader
Cargo.tomlに以下を追加:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
- データ構造の定義(5点)
src/main.rs:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct Person {
name: String,
age: u32,
email: String,
}
// TODO: 実装
- JSON読み込みの実装(10点)
以下の機能を実装: - JSONファイルから Person を読み込む - 読み込んだデータを表示 - エラーハンドリング
テスト用JSON(person.json):
{
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
期待される出力:
Name: Alice
Age: 30
Email: alice@example.com
提出物:
json_reader/Cargo.toml: 依存関係設定json_reader/src/main.rs: 実装json_reader/person.json: テストデータexecution_result.txt: 実行結果- Rustfmt(5点)
問題5:コード品質ツール(15点)
前の問題(calculator)に対して、以下のツールを実行しなさい。
cargo fmt
フォーマット前後のdiffを記録- Clippy(5点)
cargo clippy
警告があれば修正し、その内容を記録- ドキュメント生成(5点)
cargo doc --open
各関数にドキュメントコメントを追加:
/// 2つの数値を加算します。
///
/// # Examples
///
///
/// use calculator::add;
/// assert_eq!(add(2, 3), 5);
/// pub fn add(a: i32, b: i32) -> i32 {
a + b
}
提出物:
clippy_report.txt: Clippyの実行結果と修正内容calculator/src/lib.rs: ドキュメントコメント追加版
問題6:プロジェクト構造(15点)
multi_moduleプロジェクトを作成し、適切なモジュール構造を実装しなさい。
要件:
multi_module/
├── Cargo.toml
└── src/
├── main.rs
├── lib.rs
├── math/
│ ├── mod.rs
│ ├── basic.rs # add, subtract
│ └── advanced.rs # power, sqrt
└── string/
├── mod.rs
└── utils.rs # reverse, to_uppercase
実装内容:
- math::basicモジュール:add, subtract関数
- math::advancedモジュール:power, sqrt関数
- string::utilsモジュール:reverse, to_uppercase関数
- lib.rs:各モジュールを公開
- main.rs:各関数を使用してデモ
提出物:
multi_module/ディレクトリ全体module_structure.txt: ツリー構造の説明
---
ボーナス課題
> ボーナス: 以下はオプションです。マンダトリー要件を完了してから挑戦してください。
ボーナス1:ワークスペースの構築(10点)
複数のクレートを含むワークスペースを作成しなさい。
構造:
my_workspace/
├── Cargo.toml # ワークスペース設定
├── core/ # 共通ライブラリ
│ ├── Cargo.toml
│ └── src/lib.rs
├── cli/ # CLIツール
│ ├── Cargo.toml
│ └── src/main.rs
└── web/ # Webサーバー
├── Cargo.toml
└── src/main.rs
要件:
- coreクレート:共通のデータ構造と関数
- cliクレート:coreを使用するCLIツール
- webクレート:coreを使用するWebサーバー(簡易版)
- ワークスペース全体のビルドとテスト
提出物:
my_workspace/ディレクトリ全体workspace_guide.md: 各クレートの説明と使い方
ボーナス2:CLIツールの完成(10点)
clapクレートを使用して、本格的なCLIツールを作成しなさい。
機能要件:
# ファイル情報を表示するツール
$ fileinfo --help
A simple file information tool
USAGE:
fileinfo [OPTIONS] <FILE>
ARGS:
<FILE> The file to analyze
OPTIONS:
-s, --size Show file size
-t, --type Show file type
-m, --modified Show last modified time
-a, --all Show all information
-h, --help Print help information
実装内容:
- clapを使用したコマンドライン引数パース
- ファイルのメタデータ取得
- 適切なエラーハンドリング
- カラー出力(colored クレート使用)
提出物:
fileinfo/プロジェクト全体usage_examples.txt: 使用例- サンプルプログラム作成
ボーナス3:ビルド最適化の実験(10点)
様々なビルド設定を試し、性能を比較しなさい。
実験内容:
- ビルドプロファイルの比較
[profile.release-small]
inherits = "release"
opt-level = "z"
lto = true
codegen-units = 1
strip = true
[profile.release-fast]
inherits = "release"
opt-level = 3
lto = "fat"
codegen-units = 1
- 測定項目
提出物:
benchmark/プロジェクトoptimization_report.md: 実験結果とグラフCargo.toml: 各種プロファイル設定- リント・フォーマットチェック
ボーナス4:CI/CDパイプラインの構築(10点)
GitHub Actionsを使用して、完全なCI/CDパイプラインを構築しなさい。
要件:
- マルチプラットフォームビルド
- テスト
- リリース自動化
提出物:
.github/workflows/ci.yml: CI設定.github/workflows/release.yml: リリース設定ci_cd_guide.md: パイプラインの説明
ボーナス5:カスタムCargoコマンド(10点)
cargo-customという独自のCargoサブコマンドを作成しなさい。
機能例:
cargo custom analyze # プロジェクトの統計を表示
cargo custom clean-all # targetと依存キャッシュを削除
cargo custom graph # 依存関係グラフを生成
実装ガイド:
- バイナリ名を
cargo-customにする - 第1引数が"custom"であることを確認
- サブコマンドに応じた処理を実装
提出物:
cargo-custom/プロジェクトREADME.md: インストールと使用方法
---
評価基準
マンダトリー部分(80点)
| 項目 | 配点 | 評価ポイント |
|---|---|---|
| 問題1:環境構築 | 10点 | 正しいインストールと確認 |
| 問題2:Hello World | 15点 | 基本的なプロジェクト操作 |
| 問題3:ライブラリ | 25点 | 関数実装とテストの質 |
| 問題4:依存関係 | 20点 | 外部クレートの正しい使用 |
| 問題5:品質ツール | 15点 | ツールの理解と活用 |
| 問題6:構造化 | 15点 | モジュール設計の適切性 |
ボーナス部分(20点)
| 項目 | 配点 | 評価ポイント |
|---|---|---|
| ボーナス1:ワークスペース | 10点 | 複数クレートの管理 |
| ボーナス2:CLIツール | 10点 | 実用的なツール作成 |
| ボーナス3:最適化実験 | 10点 | 詳細な性能分析 |
| ボーナス4:CI/CD | 10点 | 自動化パイプライン |
| ボーナス5:カスタムコマンド | 10点 | Cargoエコシステムの理解 |
注: ボーナスは最大20点まで加算されます。
---
提出方法
ファイル構成
rust-foundations-02/
├── problem1/
│ └── environment.txt
├── problem2/
│ ├── hello_rust/
│ └── build_comparison.txt
├── problem3/
│ ├── calculator/
│ └── test_results.txt
├── problem4/
│ ├── json_reader/
│ └── execution_result.txt
├── problem5/
│ ├── calculator/ # 修正版
│ └── clippy_report.txt
├── problem6/
│ ├── multi_module/
│ └── module_structure.txt
└── bonus/
├── bonus1-workspace/
├── bonus2-cli/
├── bonus3-benchmark/
├── bonus4-ci/
└── bonus5-cargo-custom/
Git管理
以下をコミットに含めないこと(.gitignore):
# ビルド成果物
target/
Cargo.lock # ライブラリの場合は含めない
# IDE
.idea/
.vscode/
*.swp
提出期限
- マンダトリー:第2章学習後、1週間以内
- ボーナス:第5章修了時まで
---
ヒント
問題2のヒント
コマンドライン引数の取得:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
// args[0]はプログラム名
// args[1]以降が引数
}
問題3のヒント
divide関数の実装:
pub fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err("Division by zero".to_string())
} else {
Ok(a / b)
}
}
問題4のヒント
ファイル読み込み:
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = fs::read_to_string("person.json")?;
let person: Person = serde_json::from_str(&data)?;
println!("{:?}", person);
Ok(())
}
ボーナス2のヒント
clapの基本的な使い方:
use clap::Parser;
#[derive(Parser)]
#[command(name = "fileinfo")]
#[command(about = "A simple file information tool")]
struct Args {
/// The file to analyze
file: String,
/// Show file size
#[arg(short, long)]
size: bool,
}
fn main() {
let args = Args::parse();
// args.file, args.sizeを使用
}
---
学習の確認
この課題を通じて、以下を理解できたか確認してください:
- [ ] Rustupによるツールチェーン管理
- [ ] Cargoの基本コマンド(build, run, test, doc)
- [ ] プロジェクト構造のベストプラクティス
- [ ] 外部クレートの使用方法
- [ ] コード品質ツール(rustfmt, clippy)
- [ ] モジュールシステム
- [ ] ワークスペースの概念
- [ ] CI/CDの基本
次の章では、Rustの基本構文(変数、型、関数)を学びます。