Pengembangan Rust dengan Claude Code: Cargo, ownership, test, dan CLI
Panduan Rust dengan Claude Code: Cargo, ownership, test, fmt, clippy, error handling, dan contoh CLI kecil.
Mulai dari tugas Rust kecil yang bisa diverifikasi
Rust terasa ketat untuk pemula karena ada ownership, borrowing, lifetime, dan error handling dengan Result. Cara paling mudah memahaminya adalah menerjemahkan konsepnya: ownership berarti siapa yang memiliki data, borrowing berarti siapa yang meminjam data untuk dibaca atau diubah, dan lifetime berarti berapa lama reference valid. Claude Code membantu jika dipakai sebagai pair programmer yang membaca error compiler, menjelaskan pilihan desain, mengedit file terbatas, lalu menjalankan check.
Di artikel ini kita membuat CLI kecil bernama tasknote. CLI ini membaca tasks.txt berisi [ ] task dan [x] task, lalu menampilkan ringkasan atau hanya task yang masih terbuka. Rujukan resmi yang dipakai adalah Rust Book tentang ownership, Cargo Book untuk membuat project, cargo test, rustfmt, Clippy, dan Claude Code overview.
Jika belum terbiasa dengan Claude Code, mulai dari panduan awal. Untuk tim, tulis aturan di CLAUDE.md best practices dan batasi aksi dengan permissions guide.
flowchart LR
Prompt["Tujuan dan batasan"]
Cargo["Project Cargo kecil"]
Compiler["Baca error ownership"]
Tests["Kunci perilaku dengan test"]
Quality["fmt dan clippy"]
Refactor["Refactor dengan diff kecil"]
Prompt --> Cargo --> Compiler --> Tests --> Quality --> Refactor
Membuat project dengan Cargo
Cargo adalah tool standar Rust untuk membuat package, build, run, test, dan mengelola dependency. Sebelum meminta Claude Code menulis aplikasi, tentukan dulu perintah verifikasinya.
cargo new tasknote --bin
cd tasknote
cargo run
Project berisi Cargo.toml dan src/main.rs. Project modern bisa memakai edition = "2024", jadi minta Claude Code membaca manifest aktual, bukan menyalin contoh lama. Contoh ini hanya memakai clap untuk argument CLI dan anyhow untuk konteks error.
[package]
name = "tasknote"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1"
clap = { version = "4", features = ["derive"] }
Prompt pertama sebaiknya meminta desain, bukan langsung implementasi.
Di project Cargo ini, buat CLI kecil bernama `tasknote`.
CLI membaca baris `[ ] task` dan `[x] task` dari `tasks.txt`.
Pertama kembalikan desain untuk `Cargo.toml`, `src/lib.rs`, dan `src/main.rs`.
Jangan edit file dulu. Jelaskan pilihan ownership dan error handling.
Langkah berhenti ini penting. Di Rust, keputusan siapa yang memiliki data dan siapa yang hanya meminjam data akan menentukan seberapa sederhana code berikutnya.
Menanyakan ownership dan borrowing
Di CLI ini, isi file dibaca sebagai String. Fungsi parse_tasks menerima &str karena hanya membaca teks. Hasil parse menjadi Vec<Task>, dan setiap task memiliki title-nya sendiri. Fungsi summarize menerima &[Task] karena hanya membaca daftar.
Jelaskan ownership pada `parse_tasks(input: &str) -> Vec<Task>` dan `summarize(tasks: &[Task]) -> String`.
Gunakan hanya contoh CLI ini.
Jelaskan `String`, `&str`, `Vec<Task>`, dan `&[Task]` untuk pemula.
Jangan menambahkan `clone()` yang tidak perlu.
Kesalahan umum adalah meminta Claude Code “memperbaiki borrow checker” lalu menerima patch yang penuh clone(). Clone kadang benar, tetapi harus ada alasan. Jika fungsi hanya membaca data, borrowing biasanya lebih tepat.
Contoh CLI yang bisa disalin
Letakkan logic di src/lib.rs supaya bisa dites tanpa menjalankan CLI.
// src/lib.rs
use anyhow::{Context, Result};
use std::{fs, path::Path};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Task {
pub title: String,
pub done: bool,
}
pub fn parse_tasks(input: &str) -> Vec<Task> {
input.lines().filter_map(parse_task_line).collect()
}
fn parse_task_line(line: &str) -> Option<Task> {
let trimmed = line.trim();
if let Some(title) = trimmed
.strip_prefix("[x] ")
.or_else(|| trimmed.strip_prefix("[X] "))
{
return Some(Task {
title: title.trim().to_string(),
done: true,
});
}
if let Some(title) = trimmed.strip_prefix("[ ] ") {
return Some(Task {
title: title.trim().to_string(),
done: false,
});
}
None
}
pub fn summarize(tasks: &[Task]) -> String {
let total = tasks.len();
let done = tasks.iter().filter(|task| task.done).count();
let open = total.saturating_sub(done);
format!("{total} tasks: {done} done, {open} open")
}
pub fn read_tasks(path: impl AsRef<Path>) -> Result<Vec<Task>> {
let path = path.as_ref();
let content = fs::read_to_string(path)
.with_context(|| format!("failed to read {}", path.display()))?;
Ok(parse_tasks(&content))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_markdown_style_tasks() {
let tasks = parse_tasks("[ ] write parser\n[x] add tests\n[X] run clippy\n");
assert_eq!(
tasks,
vec![
Task {
title: "write parser".to_string(),
done: false,
},
Task {
title: "add tests".to_string(),
done: true,
},
Task {
title: "run clippy".to_string(),
done: true,
},
]
);
}
#[test]
fn ignores_unrecognized_lines() {
let tasks = parse_tasks("# Sprint notes\n- plain bullet\n[ ] keep this\n");
assert_eq!(tasks.len(), 1);
assert_eq!(tasks[0].title, "keep this");
}
#[test]
fn summarizes_counts() {
let tasks = parse_tasks("[ ] one\n[x] two\n[ ] three\n");
assert_eq!(summarize(&tasks), "3 tasks: 1 done, 2 open");
}
}
src/main.rs hanya mengurus argument dan output.
// src/main.rs
use anyhow::Result;
use clap::Parser;
use std::path::PathBuf;
use tasknote::{read_tasks, summarize};
#[derive(Parser, Debug)]
#[command(name = "tasknote", about = "Summarize simple task files")]
struct Cli {
#[arg(short, long, default_value = "tasks.txt")]
file: PathBuf,
#[arg(long)]
only_open: bool,
}
fn main() -> Result<()> {
let args = Cli::parse();
let tasks = read_tasks(&args.file)?;
if args.only_open {
for task in tasks.iter().filter(|task| !task.done) {
println!("- {}", task.title);
}
} else {
println!("{}", summarize(&tasks));
}
Ok(())
}
Contoh tasks.txt:
[ ] write parser
[x] add unit tests
[ ] run clippy
Perintah verifikasi:
cargo fmt
cargo test
cargo clippy --all-targets -- -D warnings
cargo run -- --file tasks.txt
cargo run -- --file tasks.txt --only-open
Karena main mengembalikan Result<()>, file yang hilang bisa dilaporkan dengan path yang jelas. Untuk CLI, input yang salah adalah error yang bisa dipulihkan, bukan alasan untuk unwrap() tanpa konteks.
Masukkan test, fmt, dan Clippy ke task
Mintalah Claude Code menjalankan check setelah edit.
Implementasikan `src/lib.rs` dan `src/main.rs`.
Setelah edit, jalankan `cargo fmt`, `cargo test`, dan `cargo clippy --all-targets -- -D warnings`.
Jika gagal, ringkas error terlebih dahulu, lalu perbaiki dengan diff sekecil mungkin.
Jangan gunakan `unwrap()` di luar test.
Dokumentasi resmi menjelaskan Claude Code sebagai agentic coding tool yang dapat membaca codebase, mengedit file, dan menjalankan command. Namun review tetap perlu dilakukan manusia: baca git diff, cek file yang tersentuh, dan ulangi command penting bila risikonya tinggi.
Use case praktis
Use case pertama adalah menambah fitur kecil pada CLI Rust yang sudah ada. Untuk --json, minta Claude Code menjaga summarize tetap stabil dan menulis formatter JSON terpisah.
Use case kedua adalah belajar dari error ownership. Saat muncul cannot move out of atau borrowed value does not live long enough, kirim error lengkap dan minta penjelasan siapa yang memiliki data dan siapa yang meminjam.
Use case ketiga adalah bug fix dengan test lebih dulu. Tambahkan test untuk baris kosong, [X], dan baris invalid sebelum mengubah parser.
Use case keempat adalah refactoring aman. Saat main.rs membesar, pisahkan parsing, file I/O, dan output tanpa mengubah signature publik.
Jebakan umum
Jangan menyelesaikan semua masalah borrowing dengan clone(). Tanyakan apakah borrowing cukup.
Jangan meninggalkan unwrap() di jalur production CLI. File, konfigurasi, dan input user bisa gagal.
Jangan refactor tanpa test. Claude Code bisa membuat diff besar dengan cepat, tetapi review menjadi sulit.
Jangan menjalankan format global pada workspace bersama jika task hanya menyentuh satu crate.
Prompt refactoring yang aman
Refactor parser `tasknote` dengan aman.
Batasan:
- Pertahankan makna `[ ] task` dan `[x] task`
- Jangan ubah signature publik `parse_tasks`, `summarize`, dan `read_tasks`
- Sentuh hanya `src/lib.rs` dan test-nya
- Jangan tambah `clone()` yang tidak perlu
- Berikan rencana 3 baris dulu dan tunggu persetujuan
Setelah edit:
- Jalankan `cargo fmt`
- Jalankan `cargo test`
- Jalankan `cargo clippy --all-targets -- -D warnings`
- Ringkas diff dan keputusan ownership
Prompt seperti ini memberi batas, check, dan format laporan. Rust cocok dengan workflow ini karena compiler, test, dan Clippy memberi feedback cepat.
CTA dan langkah berikutnya
Untuk latihan pribadi, tambah output JSON, export CSV, scan direktori, atau format berbasis serde. Untuk tim, tulis edition Rust, command wajib, kebijakan unwrap(), crate yang boleh dipakai, dan checklist review di CLAUDE.md.
ClaudeCodeLab menyediakan prompt, template, setup guide, dan training untuk Claude Code. Mulai dari cheatsheet gratis, lihat produk dan template, atau gunakan training dan konsultasi untuk menerapkannya ke repository nyata. Baca juga TDD dengan Claude Code dan review workflow checklist.
Catatan hasil uji: alur paling stabil adalah menaruh logic di src/lib.rs, mengunci behavior dengan cargo test, lalu meminta Claude Code menjelaskan ownership sebelum edit. Hasilnya diff lebih kecil dan clone() yang tidak perlu lebih sedikit.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.