Claude Code Rust with Claude Code
Learn about claude code rust using Claude Code. Practical tips and code examples included.
Rust開発でClaude Codeが活躍する場面
Rustは所有権、ライフタイム、トレイトなど独自の概念が多く、学習コストの高い言語です。Claude Codeはこれらの概念を正確に理解し、コンパイルが通るコードを高い精度で生成できます。特にエラーハンドリングや非同期処理の定型パターンで威力を発揮します。
CLIツールの開発
> clapを使ったCLIツールを作成して。
> JSONファイルを読み込んでCSVに変換する機能で。
> プログレスバーとエラーハンドリングも実装して。
// src/main.rs
use clap::Parser;
use anyhow::{Context, Result};
use indicatif::{ProgressBar, ProgressStyle};
use serde_json::Value;
use std::fs;
use std::io::Write;
#[derive(Parser, Debug)]
#[command(name = "json2csv", about = "Convert JSON to CSV")]
struct Args {
/// Input JSON file path
#[arg(short, long)]
input: String,
/// Output CSV file path
#[arg(short, long)]
output: String,
/// Fields to extract (comma-separated)
#[arg(short, long)]
fields: Option<String>,
}
fn main() -> Result<()> {
let args = Args::parse();
let content = fs::read_to_string(&args.input)
.context(format!("Failed to read file: {}", args.input))?;
let data: Vec<Value> = serde_json::from_str(&content)
.context("Failed to parse JSON")?;
if data.is_empty() {
println!("No data found in the JSON file.");
return Ok(());
}
// フィールドの決定
let fields: Vec<String> = match &args.fields {
Some(f) => f.split(',').map(|s| s.trim().to_string()).collect(),
None => {
if let Some(obj) = data[0].as_object() {
obj.keys().cloned().collect()
} else {
anyhow::bail!("JSON array elements must be objects");
}
}
};
let pb = ProgressBar::new(data.len() as u64);
pb.set_style(
ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})"
)?
.progress_chars("=>-"),
);
let mut output = fs::File::create(&args.output)
.context(format!("Failed to create output file: {}", args.output))?;
// Header
writeln!(output, "{}", fields.join(","))?;
// データ行
for item in &data {
let row: Vec<String> = fields.iter().map(|field| {
match item.get(field) {
Some(Value::String(s)) => format!("\"{}\"", s.replace('"', "\"\"")),
Some(v) => v.to_string(),
None => String::new(),
}
}).collect();
writeln!(output, "{}", row.join(","))?;
pb.inc(1);
}
pb.finish_with_message("Done");
println!("Converted {} records to {}", data.len(), args.output);
Ok(())
}
# Cargo.toml
[package]
name = "json2csv"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4", features = ["derive"] }
serde_json = "1"
anyhow = "1"
indicatif = "0.17"
Web APIの開発(Axum)
> Axumを使ったREST APIを作成して。
> CRUD操作、バリデーション、エラーハンドリングを実装して。
// src/main.rs
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post, put, delete},
Json, Router,
};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
struct Task {
id: Uuid,
title: String,
description: Option<String>,
completed: bool,
created_at: chrono::NaiveDateTime,
}
#[derive(Debug, Deserialize)]
struct CreateTask {
title: String,
description: Option<String>,
}
#[derive(Debug, Deserialize)]
struct UpdateTask {
title: Option<String>,
description: Option<String>,
completed: Option<bool>,
}
#[derive(Clone)]
struct AppState {
db: PgPool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let db = PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
sqlx::migrate!().run(&db).await?;
let state = AppState { db };
let app = Router::new()
.route("/api/tasks", get(list_tasks).post(create_task))
.route("/api/tasks/:id", get(get_task).put(update_task).delete(delete_task))
.with_state(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("Server running on http://localhost:3000");
axum::serve(listener, app).await?;
Ok(())
}
async fn list_tasks(State(state): State<AppState>) -> impl IntoResponse {
let tasks = sqlx::query_as::<_, Task>("SELECT * FROM tasks ORDER BY created_at DESC")
.fetch_all(&state.db)
.await;
match tasks {
Ok(tasks) => Json(tasks).into_response(),
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
async fn create_task(
State(state): State<AppState>,
Json(input): Json<CreateTask>,
) -> impl IntoResponse {
if input.title.trim().is_empty() {
return (StatusCode::BAD_REQUEST, "Title is required").into_response();
}
let task = sqlx::query_as::<_, Task>(
"INSERT INTO tasks (id, title, description) VALUES ($1, $2, $3) RETURNING *"
)
.bind(Uuid::new_v4())
.bind(&input.title)
.bind(&input.description)
.fetch_one(&state.db)
.await;
match task {
Ok(task) => (StatusCode::CREATED, Json(task)).into_response(),
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
async fn get_task(
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> impl IntoResponse {
let task = sqlx::query_as::<_, Task>("SELECT * FROM tasks WHERE id = $1")
.bind(id)
.fetch_optional(&state.db)
.await;
match task {
Ok(Some(task)) => Json(task).into_response(),
Ok(None) => StatusCode::NOT_FOUND.into_response(),
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
async fn update_task(
State(state): State<AppState>,
Path(id): Path<Uuid>,
Json(input): Json<UpdateTask>,
) -> impl IntoResponse {
let task = sqlx::query_as::<_, Task>(
"UPDATE tasks SET title = COALESCE($2, title), description = COALESCE($3, description), completed = COALESCE($4, completed) WHERE id = $1 RETURNING *"
)
.bind(id)
.bind(&input.title)
.bind(&input.description)
.bind(input.completed)
.fetch_optional(&state.db)
.await;
match task {
Ok(Some(task)) => Json(task).into_response(),
Ok(None) => StatusCode::NOT_FOUND.into_response(),
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
async fn delete_task(
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> impl IntoResponse {
let result = sqlx::query("DELETE FROM tasks WHERE id = $1")
.bind(id)
.execute(&state.db)
.await;
match result {
Ok(r) if r.rows_affected() > 0 => StatusCode::NO_CONTENT.into_response(),
Ok(_) => StatusCode::NOT_FOUND.into_response(),
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
}
}
エラーハンドリングのパターン
Claude Codeに依頼する際、Rustらしいエラーハンドリングを指示できます。
> カスタムエラー型をthiserrorで定義して。
> APIレスポンスへの変換も実装して。
use axum::{http::StatusCode, response::IntoResponse, Json};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Not found: {0}")]
NotFound(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
#[error("Internal error: {0}")]
Internal(#[from] anyhow::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let (status, message) = match &self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
AppError::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Database error".into()),
AppError::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into()),
};
(status, Json(serde_json::json!({ "error": message }))).into_response()
}
}
Zusammenfassung
Claude Codeを使えば、Rustの所有権やライフタイムを正しく扱ったコードを効率的に生成できます。CLIツール、Web API、エラーハンドリングなど、Rust特有のパターンに精通しているため、コンパイルエラーとの戦いを大幅に減らせます。他の言語との比較はClaude Code vs GitHub Copilotを参照してください。効率的な使い方は生産性を3倍にするTipsで紹介しています。
Claude Codeの詳細はAnthropic公式ドキュメントをご覧ください。Rustの学習にはThe Rust Programming Languageが最適です。
Related Posts
So beschleunigen Sie Ihre Nebenprojekte mit Claude Code [Mit Beispielen]
Erfahren Sie, wie Sie persönliche Entwicklungsprojekte mit Claude Code drastisch beschleunigen. Inklusive realer Beispiele und eines praktischen Workflows von der Idee bis zum Deployment.
So automatisieren Sie Refactoring mit Claude Code
Erfahren Sie, wie Sie Code-Refactoring mit Claude Code effizient automatisieren. Inklusive praktischer Prompts und konkreter Refactoring-Muster für reale Projekte.
Vollständiger CORS-Konfigurationsleitfaden mit Claude Code
Erfahren Sie alles über die CORS-Konfiguration mit Claude Code. Mit praktischen Tipps und Codebeispielen.