mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-18 06:43:05 +08:00
docs: fix zh-CN parity — add 44 missing files to ja-JP
Add files present in zh-CN but missing from ja-JP: - commands: claw, context-budget, devfleet, docs, projects, prompt-optimize, rules-distill (7 files) - skills: regex-vs-llm-structured-text, remotion-video-creation, repo-scan, research-ops, returns-reverse-logistics, rules-distill, rust-patterns, rust-testing, skill-comply, skill-stocktake, social-graph-ranker, swift-actor-persistence, swift-concurrency-6-2, swift-protocol-di-testing, swiftui-patterns, team-builder, terminal-ops, token-budget-advisor, ui-demo, unified-notifications-ops, video-editing, videodb (+reference/*), visa-doc-translate, workspace-surface-audit, x-api (37 files) Result: ja-JP now has 517 files vs zh-CN 412 files. zh-CN parity: 0 missing files (complete parity achieved).
This commit is contained in:
499
docs/ja-JP/skills/rust-patterns/SKILL.md
Normal file
499
docs/ja-JP/skills/rust-patterns/SKILL.md
Normal file
@@ -0,0 +1,499 @@
|
||||
---
|
||||
name: rust-patterns
|
||||
description: 慣用的なRustパターン、所有権、エラー処理、トレイト、並行処理、および安全で高性能なアプリケーションを構築するためのベストプラクティス。
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Rust 開発パターン
|
||||
|
||||
安全で高性能かつ保守性の高いアプリケーションを構築するための慣用的なRustパターンとベストプラクティス。
|
||||
|
||||
## 使用場面
|
||||
|
||||
* 新しいRustコードを書く場合
|
||||
* Rustコードをレビューする場合
|
||||
* 既存のRustコードをリファクタリングする場合
|
||||
* クレート構造とモジュールレイアウトを設計する場合
|
||||
|
||||
## 動作原理
|
||||
|
||||
このスキルは6つの重要な領域で慣用的なRustの規約を強制する:コンパイル時のデータ競合防止のための所有権と借用、ライブラリでは`thiserror`、アプリケーションでは`anyhow`を使用した`Result`/`?`エラー伝播、不正な状態を表現不可能にする列挙型と完全パターンマッチング、ゼロコスト抽象化のためのトレイトとジェネリクス、`Arc<Mutex<T>>`、チャンネル、async/awaitによる安全な並行処理、ドメインで整理された最小化された`pub`インターフェース。
|
||||
|
||||
## コア原則
|
||||
|
||||
### 1. 所有権と借用
|
||||
|
||||
Rustの所有権システムはコンパイル時にデータ競合とメモリエラーを防ぐ。
|
||||
|
||||
```rust
|
||||
// Good: Pass references when you don't need ownership
|
||||
fn process(data: &[u8]) -> usize {
|
||||
data.len()
|
||||
}
|
||||
|
||||
// Good: Take ownership only when you need to store or consume
|
||||
fn store(data: Vec<u8>) -> Record {
|
||||
Record { payload: data }
|
||||
}
|
||||
|
||||
// Bad: Cloning unnecessarily to avoid borrow checker
|
||||
fn process_bad(data: &Vec<u8>) -> usize {
|
||||
let cloned = data.clone(); // Wasteful — just borrow
|
||||
cloned.len()
|
||||
}
|
||||
```
|
||||
|
||||
### 柔軟な所有権のための `Cow` の使用
|
||||
|
||||
```rust
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn normalize(input: &str) -> Cow<'_, str> {
|
||||
if input.contains(' ') {
|
||||
Cow::Owned(input.replace(' ', "_"))
|
||||
} else {
|
||||
Cow::Borrowed(input) // Zero-cost when no mutation needed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## エラー処理
|
||||
|
||||
### `Result` と `?` を使用する——本番環境では `unwrap()` を絶対に使わない
|
||||
|
||||
```rust
|
||||
// Good: Propagate errors with context
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
fn load_config(path: &str) -> Result<Config> {
|
||||
let content = std::fs::read_to_string(path)
|
||||
.with_context(|| format!("failed to read config from {path}"))?;
|
||||
let config: Config = toml::from_str(&content)
|
||||
.with_context(|| format!("failed to parse config from {path}"))?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
// Bad: Panics on error
|
||||
fn load_config_bad(path: &str) -> Config {
|
||||
let content = std::fs::read_to_string(path).unwrap(); // Panics!
|
||||
toml::from_str(&content).unwrap()
|
||||
}
|
||||
```
|
||||
|
||||
### ライブラリエラーには `thiserror`、アプリケーションエラーには `anyhow`
|
||||
|
||||
```rust
|
||||
// Library code: structured, typed errors
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum StorageError {
|
||||
#[error("record not found: {id}")]
|
||||
NotFound { id: String },
|
||||
#[error("connection failed")]
|
||||
Connection(#[from] std::io::Error),
|
||||
#[error("invalid data: {0}")]
|
||||
InvalidData(String),
|
||||
}
|
||||
|
||||
// Application code: flexible error handling
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let config = load_config("app.toml")?;
|
||||
if config.workers == 0 {
|
||||
bail!("worker count must be > 0");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### ネストしたマッチの代わりに `Option` コンビネーターを優先する
|
||||
|
||||
```rust
|
||||
// Good: Combinator chain
|
||||
fn find_user_email(users: &[User], id: u64) -> Option<String> {
|
||||
users.iter()
|
||||
.find(|u| u.id == id)
|
||||
.map(|u| u.email.clone())
|
||||
}
|
||||
|
||||
// Bad: Deeply nested matching
|
||||
fn find_user_email_bad(users: &[User], id: u64) -> Option<String> {
|
||||
match users.iter().find(|u| u.id == id) {
|
||||
Some(user) => match &user.email {
|
||||
email => Some(email.clone()),
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 列挙型とパターンマッチング
|
||||
|
||||
### 状態を列挙型としてモデル化する
|
||||
|
||||
```rust
|
||||
// Good: Impossible states are unrepresentable
|
||||
enum ConnectionState {
|
||||
Disconnected,
|
||||
Connecting { attempt: u32 },
|
||||
Connected { session_id: String },
|
||||
Failed { reason: String, retries: u32 },
|
||||
}
|
||||
|
||||
fn handle(state: &ConnectionState) {
|
||||
match state {
|
||||
ConnectionState::Disconnected => connect(),
|
||||
ConnectionState::Connecting { attempt } if *attempt > 3 => abort(),
|
||||
ConnectionState::Connecting { .. } => wait(),
|
||||
ConnectionState::Connected { session_id } => use_session(session_id),
|
||||
ConnectionState::Failed { retries, .. } if *retries < 5 => retry(),
|
||||
ConnectionState::Failed { reason, .. } => log_failure(reason),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 完全マッチング——ビジネスロジックではワイルドカードを使わない
|
||||
|
||||
```rust
|
||||
// Good: Handle every variant explicitly
|
||||
match command {
|
||||
Command::Start => start_service(),
|
||||
Command::Stop => stop_service(),
|
||||
Command::Restart => restart_service(),
|
||||
// Adding a new variant forces handling here
|
||||
}
|
||||
|
||||
// Bad: Wildcard hides new variants
|
||||
match command {
|
||||
Command::Start => start_service(),
|
||||
_ => {} // Silently ignores Stop, Restart, and future variants
|
||||
}
|
||||
```
|
||||
|
||||
## トレイトとジェネリクス
|
||||
|
||||
### ジェネリックを受け取り、具体的な型を返す
|
||||
|
||||
```rust
|
||||
// Good: Generic input, concrete output
|
||||
fn read_all(reader: &mut impl Read) -> std::io::Result<Vec<u8>> {
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
// Good: Trait bounds for multiple constraints
|
||||
fn process<T: Display + Send + 'static>(item: T) -> String {
|
||||
format!("processed: {item}")
|
||||
}
|
||||
```
|
||||
|
||||
### 動的ディスパッチにトレイトオブジェクトを使用する
|
||||
|
||||
```rust
|
||||
// Use when you need heterogeneous collections or plugin systems
|
||||
trait Handler: Send + Sync {
|
||||
fn handle(&self, request: &Request) -> Response;
|
||||
}
|
||||
|
||||
struct Router {
|
||||
handlers: Vec<Box<dyn Handler>>,
|
||||
}
|
||||
|
||||
// Use generics when you need performance (monomorphization)
|
||||
fn fast_process<H: Handler>(handler: &H, request: &Request) -> Response {
|
||||
handler.handle(request)
|
||||
}
|
||||
```
|
||||
|
||||
### 型安全のためにNewTypeパターンを使用する
|
||||
|
||||
```rust
|
||||
// Good: Distinct types prevent mixing up arguments
|
||||
struct UserId(u64);
|
||||
struct OrderId(u64);
|
||||
|
||||
fn get_order(user: UserId, order: OrderId) -> Result<Order> {
|
||||
// Can't accidentally swap user and order IDs
|
||||
todo!()
|
||||
}
|
||||
|
||||
// Bad: Easy to swap arguments
|
||||
fn get_order_bad(user_id: u64, order_id: u64) -> Result<Order> {
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
## 構造体とデータモデリング
|
||||
|
||||
### 複雑な構築にはビルダーパターンを使用する
|
||||
|
||||
```rust
|
||||
struct ServerConfig {
|
||||
host: String,
|
||||
port: u16,
|
||||
max_connections: usize,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
|
||||
ServerConfigBuilder { host: host.into(), port, max_connections: 100 }
|
||||
}
|
||||
}
|
||||
|
||||
struct ServerConfigBuilder { host: String, port: u16, max_connections: usize }
|
||||
|
||||
impl ServerConfigBuilder {
|
||||
fn max_connections(mut self, n: usize) -> Self { self.max_connections = n; self }
|
||||
fn build(self) -> ServerConfig {
|
||||
ServerConfig { host: self.host, port: self.port, max_connections: self.max_connections }
|
||||
}
|
||||
}
|
||||
|
||||
// Usage: ServerConfig::builder("localhost", 8080).max_connections(200).build()
|
||||
```
|
||||
|
||||
## イテレーターとクロージャー
|
||||
|
||||
### 手動ループの代わりにイテレーターチェーンを優先する
|
||||
|
||||
```rust
|
||||
// Good: Declarative, lazy, composable
|
||||
let active_emails: Vec<String> = users.iter()
|
||||
.filter(|u| u.is_active)
|
||||
.map(|u| u.email.clone())
|
||||
.collect();
|
||||
|
||||
// Bad: Imperative accumulation
|
||||
let mut active_emails = Vec::new();
|
||||
for user in &users {
|
||||
if user.is_active {
|
||||
active_emails.push(user.email.clone());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 型注釈付きの `collect()` を使用する
|
||||
|
||||
```rust
|
||||
// Collect into different types
|
||||
let names: Vec<_> = items.iter().map(|i| &i.name).collect();
|
||||
let lookup: HashMap<_, _> = items.iter().map(|i| (i.id, i)).collect();
|
||||
let combined: String = parts.iter().copied().collect();
|
||||
|
||||
// Collect Results — short-circuits on first error
|
||||
let parsed: Result<Vec<i32>, _> = strings.iter().map(|s| s.parse()).collect();
|
||||
```
|
||||
|
||||
## 並行処理
|
||||
|
||||
### 共有可変状態には `Arc<Mutex<T>>` を使用する
|
||||
|
||||
```rust
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
let counter = Arc::new(Mutex::new(0));
|
||||
let handles: Vec<_> = (0..10).map(|_| {
|
||||
let counter = Arc::clone(&counter);
|
||||
std::thread::spawn(move || {
|
||||
let mut num = counter.lock().expect("mutex poisoned");
|
||||
*num += 1;
|
||||
})
|
||||
}).collect();
|
||||
|
||||
for handle in handles {
|
||||
handle.join().expect("worker thread panicked");
|
||||
}
|
||||
```
|
||||
|
||||
### メッセージパッシングにチャンネルを使用する
|
||||
|
||||
```rust
|
||||
use std::sync::mpsc;
|
||||
|
||||
let (tx, rx) = mpsc::sync_channel(16); // Bounded channel with backpressure
|
||||
|
||||
for i in 0..5 {
|
||||
let tx = tx.clone();
|
||||
std::thread::spawn(move || {
|
||||
tx.send(format!("message {i}")).expect("receiver disconnected");
|
||||
});
|
||||
}
|
||||
drop(tx); // Close sender so rx iterator terminates
|
||||
|
||||
for msg in rx {
|
||||
println!("{msg}");
|
||||
}
|
||||
```
|
||||
|
||||
### Tokioを使用した非同期プログラミング
|
||||
|
||||
```rust
|
||||
use tokio::time::Duration;
|
||||
|
||||
async fn fetch_with_timeout(url: &str) -> Result<String> {
|
||||
let response = tokio::time::timeout(
|
||||
Duration::from_secs(5),
|
||||
reqwest::get(url),
|
||||
)
|
||||
.await
|
||||
.context("request timed out")?
|
||||
.context("request failed")?;
|
||||
|
||||
response.text().await.context("failed to read body")
|
||||
}
|
||||
|
||||
// Spawn concurrent tasks
|
||||
async fn fetch_all(urls: Vec<String>) -> Vec<Result<String>> {
|
||||
let handles: Vec<_> = urls.into_iter()
|
||||
.map(|url| tokio::spawn(async move {
|
||||
fetch_with_timeout(&url).await
|
||||
}))
|
||||
.collect();
|
||||
|
||||
let mut results = Vec::with_capacity(handles.len());
|
||||
for handle in handles {
|
||||
results.push(handle.await.unwrap_or_else(|e| panic!("spawned task panicked: {e}")));
|
||||
}
|
||||
results
|
||||
}
|
||||
```
|
||||
|
||||
## アンセーフコード
|
||||
|
||||
### Unsafe を使用できる場合
|
||||
|
||||
```rust
|
||||
// Acceptable: FFI boundary with documented invariants (Rust 2024+)
|
||||
/// # Safety
|
||||
/// `ptr` must be a valid, aligned pointer to an initialized `Widget`.
|
||||
unsafe fn widget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {
|
||||
// SAFETY: caller guarantees ptr is valid and aligned
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
|
||||
// Acceptable: Performance-critical path with proof of correctness
|
||||
// SAFETY: index is always < len due to the loop bound
|
||||
unsafe { slice.get_unchecked(index) }
|
||||
```
|
||||
|
||||
### Unsafe を使用してはいけない場合
|
||||
|
||||
```rust
|
||||
// Bad: Using unsafe to bypass borrow checker
|
||||
// Bad: Using unsafe for convenience
|
||||
// Bad: Using unsafe without a Safety comment
|
||||
// Bad: Transmuting between unrelated types
|
||||
```
|
||||
|
||||
## モジュールシステムとクレート構造
|
||||
|
||||
### 型ではなくドメインで整理する
|
||||
|
||||
```text
|
||||
my_app/
|
||||
├── src/
|
||||
│ ├── main.rs
|
||||
│ ├── lib.rs
|
||||
│ ├── auth/ # ドメインモジュール
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── token.rs
|
||||
│ │ └── middleware.rs
|
||||
│ ├── orders/ # ドメインモジュール
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── model.rs
|
||||
│ │ └── service.rs
|
||||
│ └── db/ # インフラストラクチャ
|
||||
│ ├── mod.rs
|
||||
│ └── pool.rs
|
||||
├── tests/ # 統合テスト
|
||||
├── benches/ # ベンチマーク
|
||||
└── Cargo.toml
|
||||
```
|
||||
|
||||
### 可視性——露出を最小化する
|
||||
|
||||
```rust
|
||||
// Good: pub(crate) for internal sharing
|
||||
pub(crate) fn validate_input(input: &str) -> bool {
|
||||
!input.is_empty()
|
||||
}
|
||||
|
||||
// Good: Re-export public API from lib.rs
|
||||
pub mod auth;
|
||||
pub use auth::AuthMiddleware;
|
||||
|
||||
// Bad: Making everything pub
|
||||
pub fn internal_helper() {} // Should be pub(crate) or private
|
||||
```
|
||||
|
||||
## ツール統合
|
||||
|
||||
### 基本コマンド
|
||||
|
||||
```bash
|
||||
# Build and check
|
||||
cargo build
|
||||
cargo check # Fast type checking without codegen
|
||||
cargo clippy # Lints and suggestions
|
||||
cargo fmt # Format code
|
||||
|
||||
# Testing
|
||||
cargo test
|
||||
cargo test -- --nocapture # Show println output
|
||||
cargo test --lib # Unit tests only
|
||||
cargo test --test integration # Integration tests only
|
||||
|
||||
# Dependencies
|
||||
cargo audit # Security audit
|
||||
cargo tree # Dependency tree
|
||||
cargo update # Update dependencies
|
||||
|
||||
# Performance
|
||||
cargo bench # Run benchmarks
|
||||
```
|
||||
|
||||
## クイックリファレンス:Rustのイディオム
|
||||
|
||||
| イディオム | 説明 |
|
||||
|-------|-------------|
|
||||
| 借用、クローンではなく | 所有権が必要でなければ `&T` を渡し、クローンしない |
|
||||
| 不正な状態を表現不可能にする | 列挙型を使用して有効な状態のみをモデル化する |
|
||||
| `unwrap()` より `?` | エラーを伝播し、ライブラリ/本番コードでパニックしない |
|
||||
| 検証より解析 | 境界で非構造化データを型付き構造体に変換する |
|
||||
| 型安全のためのNewtype | 基本型をnewtypeでラップして引数の取り違えを防ぐ |
|
||||
| ループよりイテレーターを優先 | 宣言的なチェーンはより明確で通常より高速 |
|
||||
| Resultに `#[must_use]` を使用 | 呼び出し元が戻り値を処理することを保証する |
|
||||
| 柔軟な所有権のために `Cow` を使用 | 借用で十分な場合にアロケーションを避ける |
|
||||
| 完全マッチング | ビジネスクリティカルな列挙型でワイルドカード `_` を使わない |
|
||||
| `pub` インターフェースを最小化 | 内部APIには `pub(crate)` を使用 |
|
||||
|
||||
## 避けるべきアンチパターン
|
||||
|
||||
```rust
|
||||
// Bad: .unwrap() in production code
|
||||
let value = map.get("key").unwrap();
|
||||
|
||||
// Bad: .clone() to satisfy borrow checker without understanding why
|
||||
let data = expensive_data.clone();
|
||||
process(&original, &data);
|
||||
|
||||
// Bad: Using String when &str suffices
|
||||
fn greet(name: String) { /* should be &str */ }
|
||||
|
||||
// Bad: Box<dyn Error> in libraries (use thiserror instead)
|
||||
fn parse(input: &str) -> Result<Data, Box<dyn std::error::Error>> { todo!() }
|
||||
|
||||
// Bad: Ignoring must_use warnings
|
||||
let _ = validate(input); // Silently discarding a Result
|
||||
|
||||
// Bad: Blocking in async context
|
||||
async fn bad_async() {
|
||||
std::thread::sleep(Duration::from_secs(1)); // Blocks the executor!
|
||||
// Use: tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
```
|
||||
|
||||
**覚えておくこと**:コンパイルが通れば、おそらく正しい——ただし `unwrap()` を避け、`unsafe` を最小化し、型システムを活用することが前提。
|
||||
Reference in New Issue
Block a user