課題22: マルチクレートプロジェクト
マンダトリー要件
問題1: Cargo機能の理解(15点)
以下の質問に答えなさい。
- ワークスペースの利点(5点)
- Features(5点)
#[cfg(feature = "...")]の使い方を例とともに示すこと。- ビルドプロファイル(5点)
devとreleaseプロファイルの違いを説明しなさい。
- カスタムプロファイルが有用な場面を2つ挙げなさい。問題2: タスク管理システムの構築(40点)
ワークスペースを使ったタスク管理システムを構築しなさい。
2.1 ワークスペース構成(15点)
task-manager/
├── Cargo.toml # ワークスペースルート
├── core/
│ ├── Cargo.toml
│ └── src/lib.rs # コアロジック
├── cli/
│ ├── Cargo.toml
│ └── src/main.rs # CLIアプリ
├── api/
│ ├── Cargo.toml
│ └── src/main.rs # APIサーバー
└── storage/
├── Cargo.toml
└── src/lib.rs # ストレージ抽象化
ワークスペースルートのCargo.toml:
[workspace]
members = [
"core",
"cli",
"api",
"storage",
]
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
chrono = "0.4"
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
要件:
- 4つのクレートを持つワークスペース
- 共通の依存関係をworkspace.dependenciesで管理
- 各クレート間の依存関係を適切に設定
2.2 Coreクレート(10点)
// core/src/lib.rs
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
pub id: u64,
pub title: String,
pub description: String,
pub completed: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Priority {
Low,
Medium,
High,
}
pub trait TaskRepository {
fn create(&mut self, task: Task) -> Result<Task, String>;
fn get(&self, id: u64) -> Result<Option<Task>, String>;
fn list(&self) -> Result<Vec<Task>, String>;
fn update(&mut self, task: Task) -> Result<Task, String>;
fn delete(&mut self, id: u64) -> Result<(), String>;
}
2.3 Storageクレート(10点)
// storage/src/lib.rs
use core::{Task, TaskRepository};
use std::collections::HashMap;
pub struct MemoryStorage {
tasks: HashMap<u64, Task>,
next_id: u64,
}
impl MemoryStorage {
pub fn new() -> Self {
MemoryStorage {
tasks: HashMap::new(),
next_id: 1,
}
}
}
impl TaskRepository for MemoryStorage {
// 実装
}
#[cfg(feature = "json")]
pub struct JsonStorage {
file_path: String,
// 実装
}
#[cfg(feature = "sqlite")]
pub struct SqliteStorage {
// 実装
}
Cargo.toml:
[package]
name = "storage"
version.workspace = true
edition.workspace = true
[dependencies]
core = { path = "../core" }
serde.workspace = true
serde_json = { version = "1.0", optional = true }
rusqlite = { version = "0.29", optional = true }
[features]
json = ["serde_json"]
sqlite = ["rusqlite"]
2.4 CLIアプリ(5点)
// cli/src/main.rs
use core::{Task, TaskRepository};
use storage::MemoryStorage;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "task")]
#[command(about = "A simple task manager")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Add { title: String },
List,
Complete { id: u64 },
Delete { id: u64 },
}
fn main() {
// 実装
}
問題3: Features実装(25点)
3.1 複数ストレージバックエンド(15点)
以下のfeaturesを実装しなさい:
# storage/Cargo.toml
[features]
default = ["memory"]
memory = []
json = ["serde_json"]
sqlite = ["rusqlite"]
postgres = ["tokio-postgres"]
各バックエンドの実装要件:
- Memory: HashMap を使ったインメモリストレージ
- JSON: ファイルベースのJSONストレージ
- SQLite: SQLiteデータベース
- PostgreSQL: PostgreSQLデータベース(ボーナス)
3.2 条件付きコンパイル(10点)
// storage/src/lib.rs
#[cfg(feature = "memory")]
pub mod memory;
#[cfg(feature = "json")]
pub mod json;
#[cfg(feature = "sqlite")]
pub mod sqlite;
// デフォルトバックエンドの選択
#[cfg(feature = "memory")]
pub use memory::MemoryStorage as DefaultStorage;
#[cfg(all(feature = "json", not(feature = "memory")))]
pub use json::JsonStorage as DefaultStorage;
---
ボーナス課題
ボーナス1: カスタムプロファイル(10点)
以下のカスタムプロファイルを作成しなさい:
# Cargo.toml (ワークスペースルート)
[profile.production]
inherits = "release"
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
[profile.fast-dev]
inherits = "dev"
opt-level = 1
[profile.size-optimized]
inherits = "release"
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
分析レポート:
- 各プロファイルでビルドし、バイナリサイズを比較
- ビルド時間を測定
- 実行パフォーマンスを比較
- メタデータ完備
ボーナス2: クレート公開準備(10点)
crates.ioに公開できる状態にしなさい:
[package]
name = "task-manager-core"
version = "0.1.0"
description = "Core library for task management"
license = "MIT OR Apache-2.0"
repository = "https://github.com/user/task-manager"
keywords = ["task", "todo", "productivity"]
categories = ["command-line-utilities"]
- ドキュメント
- テスト
- ライセンス
ボーナス3: ビルドスクリプト(10点)
ビルド時にバージョン情報を埋め込むビルドスクリプトを作成しなさい:
// build.rs
use std::process::Command;
fn main() {
// Gitコミットハッシュ
let output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.output()
.unwrap();
let git_hash = String::from_utf8(output.stdout).unwrap();
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
// ビルド日時
let build_time = chrono::Utc::now().to_rfc3339();
println!("cargo:rustc-env=BUILD_TIME={}", build_time);
// Rustバージョン
let rustc_version = rustc_version::version().unwrap();
println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version);
}
使用例:
// cli/src/main.rs
fn version_info() {
println!("Version: {}", env!("CARGO_PKG_VERSION"));
println!("Git: {}", env!("GIT_HASH"));
println!("Built: {}", env!("BUILD_TIME"));
println!("Rustc: {}", env!("RUSTC_VERSION"));
}
ボーナス4: マルチプラットフォームビルド(10点)
複数のプラットフォーム向けにビルドしなさい:
# GitHub Actionsを使用
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: macos-latest
target: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --release --target ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: task-manager-${{ matrix.target }}
path: target/${{ matrix.target }}/release/cli
---
評価基準
マンダトリー部分(80点)
| 項目 | 配点 | 評価ポイント |
|---|---|---|
| 問題1: 基礎理解 | 15点 | 正確な説明 |
| 問題2: ワークスペース | 40点 | 適切な構成と実装 |
| 問題3: Features | 25点 | 正しい条件付きコンパイル |
ボーナス部分(20点)
| 項目 | 配点 | 評価ポイント |
|---|---|---|
| ボーナス1: プロファイル | 10点 | 詳細な分析 |
| ボーナス2: 公開準備 | 10点 | 完全なメタデータ |
| ボーナス3: ビルドスクリプト | 10点 | 正しい実装 |
| ボーナス4: マルチプラットフォーム | 10点 | CI/CD設定 |
注: ボーナスは最大20点まで加算されます。
---
提出方法
ファイル構成
rust-foundations-22/
├── Cargo.toml
├── Cargo.lock
├── README.md
├── LICENSE-MIT
├── LICENSE-APACHE
├── CHANGELOG.md
├── answers.md # 問題1の回答
├── build.rs # ボーナス3
├── .github/
│ └── workflows/
│ └── release.yml # ボーナス4
├── core/
├── cli/
├── api/
└── storage/
テストとビルド
# すべてテスト
cargo test --workspace
# すべてビルド
cargo build --workspace --release
# 特定のfeatureでビルド
cargo build -p storage --features json
cargo build -p storage --features sqlite
# カスタムプロファイル
cargo build --profile production
---
ヒント
問題2のヒント
依存関係の設定:
# cli/Cargo.toml
[dependencies]
core = { path = "../core" }
storage = { path = "../storage" }
clap = { version = "4.0", features = ["derive"] }
問題3のヒント
Feature の組み合わせテスト:
# すべてのfeature組み合わせをテスト
cargo test --features memory
cargo test --features json
cargo test --features sqlite
cargo test --all-features
cargo test --no-default-features
---
学習の確認
この課題を通じて、以下を理解できたか確認してください:
- [ ] ワークスペースの構成と管理
- [ ] Featuresの設計と実装
- [ ] カスタムプロファイルの作成
- [ ] クレート公開の準備
- [ ] ビルドスクリプトの使用
次の章では、パフォーマンス最適化を学びます。