Rust ist kein C++ mit anderer Syntax — die Architektur muss mitwandern
Die Uebersetzung von C++-Codebase nach Rust scheitert haeufig nicht an der Syntax, sondern daran, dass die Architektur der Originalcode nicht uebertragbar ist. Drei aktuelle Papers zeigen warum — und wie man es richtig macht: das C-to-Rust Fallacy-Papier (arXiv 2609.25682), die Gap-Taxonomie arXiv 2609.XX und der Typestate-Erfahrungsbericht ("Functional State Machines in Rust", DOI 10.1145/3830438.3830958).
Warum Literal-Translation scheitert: Die Fallacy
Das "C-to-Rust Fallacy: Automatic Refactoring != Memory Safety" Paper arXiv 2609.25682 beweist: Wer C++ 1:1 nach Rust uebersetzt, bleibt mit den Architektur-Problemen des Originals zurueck. Der Grund ist einfach — Memory Safety ist in Rust kein Feature, sondern eine Konsequenz der Architektur.
In C++ drueckt sich die Architektur haeufig in Pointer-Netzwerken aus:
std::unique_ptr<Node> hier, std::shared_ptr<Child> da, raw Pointers wo die
Performance es verlangt. Diese Struktur ist nicht nur Code, sie ist das
implizite Design des Systems. Eine wortgetreue Uebersetzung mit Box<T>,
Rc<T>, Arc<T> behaelt die gleiche Architektur — und damit die gleichen
Design-Fehler.
Memory Safety durch Architektur, nicht durch Syntax
Rusts Typsystem erzwingt Memory Safety nur, wenn die Architektur entsprechend gestaltet ist. Ownership, Borrowing und Lifetimes sind keine Sprachfeatures, sondern Architektur-Primitive. Eine 1:1-Uebersetzung uebernimmt die C++-Architektur — und damit deren unsichere Annahmen.
Beispiel: Ein C++-Parser der Dokumente als pointer-basierten Baum
hierarchien representiert, wird in Rust zu einer Struktur die immer noch
auf Lifetimes und Borrows angewiesen ist. Die Loesung: den Baum als
enum-basierte State Machine modellieren. Plötzlich werden ungueltige
Zustaende nicht runtime geprüft, sondern sind unmoeglich.
Die Gap-Taxonomie: Wo C++ und Rust sich semantisch unterscheiden
"Beyond Compilation: A Taxonomy of Semantic Gaps in C-to-Rust Translation" (2026) kategorisiert die wichtigsten semantischen Unterschiede — und zeigt why eine Uebersetzung immer auch ein Redesign sein muss:
| Gap | C++ | Rust | Architektur-Folge |
|---|---|---|---|
| Pointer Aliasing | Mehrere Pointer auf dieselbe Daten | Ein Owner, viele Borrows | Datenfluss muss linear werden |
| Undefined Behaviour | Undefiniert bei Overflow, Null-Deref | Definiert oder Compile-Error | Explicite Fehlerbehandlung |
| Type Punning | reinterpret_cast | unsafe + invariants | Typ-Sicherheit wird erzwungen |
| Implicit Conversions | Implizit Integer → Float | Explicite as/From | Typ-Klarheit |
| Platform Assumptions | sizeof(int) variabel | Plattform-unabhaengig | Abstraktionsschichten |
Jede dieser Luecken verlangt eine Anpassung der Datenstrukturen und Algorithmen — nicht nur der Syntax.
Typestate: Architektur sichtbar machen
Der Typestate-Erfahrungsbericht "Functional State Machines in Rust" (DOI 10.1145/3830438.3830958) zeigt, wie Rust Zustandsautomaten als Typen modelliert. Das ist kein akademisches Spiel, sondern Architektur-Dokumentation in Code-Form.
Nehmen wir eine Bestellungs-Verarbeitung:
// Jeder Zustand ist ein eigener Typ
pub struct DraftOrder;
pub struct PaidOrder;
pub struct ShippedOrder;
pub struct CancelledOrder;
// Uebergaenge sind Methoden die *nur* auf bestimmten Typen existieren
impl DraftOrder {
pub fn pay(self, amount: Currency) -> PaidOrder { /* ... */ }
}
impl PaidOrder {
pub fn ship(self, tracking: TrackingId) -> ShippedOrder { /* ... */ }
pub fn cancel(self) -> CancelledOrder { /* ... */ }
}
// ShippedOrder hat KEIN cancel() — der Compiler verbietet es
impl ShippedOrder {
pub fn track(&self) -> TrackingInfo { /* ... */ }
}
Das eigene Typen fuer jeden Zustand hat drei Vorteile:
- Compile-Zeit-Garantie:
shipped_order.cancel()ist kein Code den man schreiben kann. Der Fehler wird zur Compile-Zeit gefangen. - Exhaustiveness: Jeder Zustand muss im Code behandelt werden. Ein neuer Zustand zwingt alle Call Sites zur Anpassung.
- Architektur-Dokumentation: Die Typen sind die Architektur. Nicht als Diagramm in Confluence, sondern als kompilierbarer Code.
World-Office als Fallstudie: Rust-Fleet
Das World-Office-Projekt hat eine proprietary C++-Codebase (ONLYOFFICE Core: x2t Parser + sdkjs Editing Model) nach Rust portiert. Das Translation-Paradigms-Dokument (World-Office/AGENTS.md) definiert sieben Paradigmen fuer diese Migration:
- Sum Types over Virtual Dispatch:
enum+matchstattBox<dyn Trait> - Ownership: clone-mutate-store: Never borrow across mutation
- Fail with Result: Kein Panic in Library-Code
- Char-index (Unicode) Semantics: Zeilen- und Zeichen-Offsets richtig
- Verify, don't trust: Jede übersetzte Einheit braucht Tests
unsafeis a gate, not a tool: Mit Invarianten und Tests- Idiomatic Rust, not transliteration:
cargo clippyclean ist Pflicht
Das Ergebnis: Eine Codebase die nicht nur memory-safe ist, sondern auch durch ihre Architektur beweisbar korrekt funktioniert.
Im World-Office Rust-Fleet wird Typestate extensively genutzt — nicht nur fuer einfache State Machines, sondern fuer ganze Dokument-Modelle. Ein Dokument kann plötzlich nicht in einem "halben Zustand" sein, weil dieser Zustand gar nicht im Typsystem existiert.
Die Marche: Wohin die Forschung geht
Die aktuelle Forschung zeigt einen Trend von "Translation" zu "Transmutation":
- Phase 1 — Translation Tools: TRACTOR (arXiv 2609.25121) als Benchmark fuer C-to-Rust Translators
- Phase 2 — Fallacy Erkennung: Automatische Erkennung von semantischen Gaps (arXiv 2609.25682)
- Phase 3 — Provable Rust:
KVerus(DOI 10.1145/3832783.3834503) undVerusSeekfuer formale Verifikation von Rust-Code
Das Ziel ist klar: Nicht nur von C++ nach Rust uebersetzen, sondern von einer unsicheren zu einer beweisbar sicheren Architektur wandern.
Fazit: Architektur vor Syntax
Rust ist kein C++ mit anderer Syntax. Rust ist eine Sprache in der Architektur sichtbar und erzwingbar ist. Wer nur die Syntax uebersetzt, verpasst den Punkt. Wer die Architektur anpasst, gewinnt nicht nur Memory Safety, sondern auch ein System das leichter zu verstehen, zu testen und zu warten ist.
Der erste Schritt ist das Eingeständnis: Dein C++-Code hat wahrscheinlich eine Architektur die nicht 1:1 auf Rust uebertragbar ist. Der zweite Schritt ist die Akzeptanz: Das ist gut so. Der dritte Schritt ist der Entschluss: Nutze die Gelegenheit, die Architektur zu verbessern.
Weiterlesen
- Blog: Rust ist kein C++ mit anderer Syntax — Warum Typ-Sicherheit eine Architektur-Entscheidung ist
- Kurs: Modul 25 — C++ zu Rust: Migration & Interop — Praktische Migration von World-Office C++-Code
- Kurs: Modul 12 — Typestate & Event-Sourcing — Typisierbare Zustandsmaschinen
Ins Thema einsteigen
Fang klein an, aber fang richtig an:
- Lies die Fallacy: arXiv 2609.25682 zeigt warum naives Uebersetzen nicht funktioniert
- Verstehe die gaps: Die Gap-Taxonomie hilft Dir die kritischen Unterschiede C++ ↔ Rust zu erkennen
- Probiere Typestate: Implementiere eine einfache State Machine mit Typestate und erlebe die Compile-Zeit-Garantien selbst
- Mache Modul 25: Migration einer realen C++-Komponente nach Rust
- Nutze TRACTOR: Das arXiv 2609.25121 Tool als Benchmark fuer Deine eigenen Translation-Experimente