Use Cases (Aktualisiert: 2.6.2026)

Rust-Entwicklung mit Claude Code: Cargo, Ownership, Tests und CLI-Refactoring

Praxisguide zu Rust mit Claude Code: Cargo, Ownership, Tests, fmt, clippy, Fehlerbehandlung und ein kleines CLI.

Rust-Entwicklung mit Claude Code: Cargo, Ownership, Tests und CLI-Refactoring

Mit einer kleinen, prüfbaren Rust-Aufgabe beginnen

Rust wirkt am Anfang streng, weil Ownership, Borrowing, Lifetimes und Fehlerbehandlung mit Result neu sind. Genau diese Strenge ist aber der Nutzen: Der Compiler zwingt dich, gefährliche Zustände früh zu klären. Claude Code hilft in Rust-Projekten am meisten, wenn du es nicht als blinden Codegenerator nutzt, sondern als Partner, der Compilerfehler erklärt, kleine Dateien editiert und anschließend Tests ausführt.

In diesem Artikel bauen wir ein kleines CLI namens tasknote. Es liest tasks.txt mit Zeilen wie [ ] task und [x] task, zeigt eine Zusammenfassung und kann offene Aufgaben ausgeben. Als Quellen verwenden wir die offiziellen Dokumente: Rust Book zu Ownership, Cargo Book zum Projektstart, cargo test, rustfmt, Clippy und Claude Code overview.

Wenn du Claude Code noch nicht sicher nutzt, lies zuerst den Getting-started-Guide. Für Teams gehören Projektregeln in CLAUDE.md best practices und Ausführungsgrenzen in den Permissions Guide.

flowchart LR
  Prompt["Ziel und Grenzen"]
  Cargo["Kleines Cargo-Projekt"]
  Compiler["Ownership-Fehler lesen"]
  Tests["Verhalten mit Tests sichern"]
  Quality["fmt und clippy"]
  Refactor["Kleines Refactoring"]

  Prompt --> Cargo --> Compiler --> Tests --> Quality --> Refactor

Cargo-Projekt anlegen

Cargo ist das Standardwerkzeug für Rust-Pakete, Builds, Ausführung, Tests und Dependencies. Definiere vor der Codegenerierung die Prüfkommandos. Dadurch bekommt Claude Code ein klares Ziel.

cargo new tasknote --bin
cd tasknote
cargo run

Das Projekt enthält Cargo.toml und src/main.rs. Aktuelle Projekte können edition = "2024" verwenden, also soll Claude Code das echte Manifest lesen und nicht alte Beispiele kopieren. Für dieses Beispiel nutzen wir nur clap für CLI-Argumente und anyhow für Fehlerkontext.

[package]
name = "tasknote"
version = "0.1.0"
edition = "2024"

[dependencies]
anyhow = "1"
clap = { version = "4", features = ["derive"] }

Der erste Prompt sollte nur Design verlangen:

In diesem Cargo-Projekt soll ein CLI `tasknote` entstehen.
Es liest `[ ] task` und `[x] task` aus `tasks.txt`.
Gib zuerst nur das Design für `Cargo.toml`, `src/lib.rs` und `src/main.rs` zurück.
Bearbeite noch keine Dateien. Erkläre Ownership und Fehlerbehandlung.

Diese Pause verhindert schlechte Struktur. In Rust entscheidet die Frage, wer Daten besitzt und wer sie nur ausleiht, über die Einfachheit des Codes.

Ownership und Borrowing gezielt fragen

Ownership bedeutet: Ein Wert hat einen Besitzer. Borrowing bedeutet: Code nutzt einen Wert vorübergehend, ohne ihn zu übernehmen. Lifetimes beschreiben, wie lange eine Referenz gültig ist. Für Einsteiger ist die beste Frage: Wer besitzt die Daten, und wer liest nur?

In tasknote wird der Dateiinhalt als String gelesen. parse_tasks bekommt nur &str, weil es den Text liest. Das Ergebnis ist Vec<Task>, und jede Task besitzt ihren Titel. summarize nimmt &[Task], weil es die Liste nur betrachtet.

Erkläre Ownership in `parse_tasks(input: &str) -> Vec<Task>` und `summarize(tasks: &[Task]) -> String`.
Nutze nur dieses CLI als Beispiel.
Erkläre `String`, `&str`, `Vec<Task>` und `&[Task]` einsteigerfreundlich.
Füge keine unnötigen `clone()`-Aufrufe hinzu.

Ein typischer Fehler ist, Borrow-Checker-Probleme mit überall verteilten clone()-Aufrufen zu überdecken. Manchmal ist ein Clone richtig, aber er braucht eine Begründung.

Kopierbares CLI-Beispiel

Die Logik kommt in src/lib.rs. So kann sie getestet werden, ohne das CLI zu starten.

// 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");
    }
}

Der Einstiegspunkt bleibt schlank.

// 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(())
}

Beispiel für tasks.txt:

[ ] write parser
[x] add unit tests
[ ] run clippy

Prüfe das Projekt so:

cargo fmt
cargo test
cargo clippy --all-targets -- -D warnings
cargo run -- --file tasks.txt
cargo run -- --file tasks.txt --only-open

main gibt Result<()> zurück. Wenn die Datei fehlt, kann anyhow::Context den fehlerhaften Pfad anzeigen. Für ein CLI ist das besser als ein panischer unwrap().

Tests, fmt und Clippy in den Auftrag schreiben

Claude Code sollte nicht nur Code liefern, sondern auch die Checks ausführen.

Implementiere `src/lib.rs` und `src/main.rs`.
Führe danach `cargo fmt`, `cargo test` und `cargo clippy --all-targets -- -D warnings` aus.
Wenn etwas fehlschlägt, fasse zuerst den Fehler zusammen und korrigiere mit kleinem Diff.
Nutze `unwrap()` nicht außerhalb von Tests.

Die offizielle Claude-Code-Dokumentation beschreibt es als agentic coding tool, das Codebases lesen, Dateien ändern und Befehle ausführen kann. Trotzdem musst du git diff prüfen und sicherstellen, dass nur die erwarteten Dateien geändert wurden.

Praktische Use Cases

Erster Use Case: Eine Option zu einem bestehenden Rust-CLI hinzufügen. Für --json soll Claude Code summarize stabil halten und eine separate Formatierungsfunktion schreiben.

Zweiter Use Case: Ownership-Fehler verstehen. Gib die vollständige Fehlermeldung weiter und frage, wer den Wert besitzt, wer ihn ausleiht und welche Referenz zu lange lebt.

Dritter Use Case: Bugs test-first beheben. Schreibe Tests für Leerzeilen, [X] und ungültige Zeilen, bevor du den Parser änderst.

Vierter Use Case: Sicher refactoren. Wenn main.rs wächst, trenne Parsing, I/O und Ausgabe, ohne öffentliche Signaturen zu ändern.

Häufige Fallen

Überdecke Borrowing-Probleme nicht mit clone() überall. Frage nach, ob Borrowing reicht.

Lasse unwrap() nicht in produktiven CLI-Pfaden. Dateien, Konfiguration und Nutzereingaben können fehlschlagen.

Refactore nicht ohne Tests. Große KI-Diffs sind schnell erstellt, aber schwer zu prüfen.

Formatiere in einem gemeinsamen Workspace nicht alles, wenn nur ein Crate betroffen ist.

Sicherer Refactoring-Prompt

Refactore den Parser von `tasknote` sicher.

Bedingungen:
- Bedeutung von `[ ] task` und `[x] task` beibehalten
- Öffentliche Signaturen von `parse_tasks`, `summarize`, `read_tasks` nicht ändern
- Nur `src/lib.rs` und seine Tests anfassen
- Keine unnötigen `clone()`-Aufrufe hinzufügen
- Zuerst einen 3-Zeilen-Plan ausgeben und auf Freigabe warten

Nach der Änderung:
- `cargo fmt`
- `cargo test`
- `cargo clippy --all-targets -- -D warnings`
- Diff und Ownership-Entscheidungen zusammenfassen

So bekommt der Agent Grenzen, Checks und Berichtspflichten. Rust passt dazu, weil Compiler, Tests und Clippy schnelle Rückmeldung geben.

CTA und nächste Schritte

Zum Üben kannst du JSON-Ausgabe, CSV-Export, Verzeichnis-Scan oder ein serde-Format ergänzen. Im Team gehören Rust edition, Pflichtbefehle, unwrap()-Regeln, erlaubte Crates und Review-Checkliste in CLAUDE.md.

ClaudeCodeLab bietet Prompts, Vorlagen, Setup-Guides und Teamtraining für Claude Code. Starte mit dem kostenlosen Cheatsheet, prüfe Produkte und Templates oder nutze Training und Beratung für dein Repository. Passende Folgeartikel sind TDD mit Claude Code und die Review-Workflow-Checkliste.

Testnotiz: Am stabilsten war der Ablauf, die Logik zuerst in src/lib.rs zu halten, Verhalten mit cargo test festzuschreiben und Claude Code Ownership erklären zu lassen, bevor es editiert. Dadurch entstehen kleinere Diffs und weniger unnötige clone()-Aufrufe.

#Claude Code #Rust #CLI #Tests #Refactoring
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.