← Alle Insights

Rust für Trading-Systeme: Sicherheit ohne Performance-Verlust.

Lange galt C++ als unangefochtene Sprache für Low-Latency-Trading. Seit einigen Jahren wächst eine ernstzunehmende Alternative heran: Rust kombiniert die Performance einer systemnahen Sprache mit Speichersicherheit, die in C++ nur durch Disziplin und teure Werkzeuge zu erreichen ist. Warum Sie das interessieren sollte — und wo die Grenzen liegen.

Warum überhaupt eine neue Sprache?

Trading-Systeme sind ein besonderer Anwendungsfall: Sie laufen unter harten zeitlichen Bedingungen, dürfen nicht crashen, müssen aber gleichzeitig schnell weiterentwickelt werden können. Jede unerwartete Pause durch Garbage Collection ist ein Risiko, jeder Segfault in der Produktion ein Albtraum, und jede Race Condition in einem Order-Router kann sechsstellige Beträge kosten.

C++ liefert die Performance, aber die Sprache kennt keine Schutzmechanismen gegen klassische Fehler wie Use-After-Free, Dangling Pointers oder Data Races. Die Industrie hat darauf mit drei Antworten reagiert: strikte Coding-Guidelines, statische Analyse und exzessives Testing. Trotzdem tauchen Speicherfehler in produktivem Code regelmäßig auf — Microsoft schätzt, dass rund 70 % aller sicherheitsrelevanten Bugs in C/C++-Codebasen auf Speicherprobleme zurückgehen.

Rust greift dieses Problem an der Wurzel an. Statt zur Laufzeit zu prüfen, prüft der Compiler bereits beim Übersetzen, ob ein Programm bestimmten Regeln für Eigentum (Ownership) und Lebenszeiten (Lifetimes) folgt. Programme, die diese Regeln verletzen, werden gar nicht erst übersetzt. Das fühlt sich am Anfang wie ein Korsett an — und ist nach ein paar Wochen ein Sicherheitsnetz, das man nicht mehr missen möchte.

Ownership, Borrowing, Lifetimes — die drei Säulen.

Das Ownership-Modell von Rust ist die größte Hürde für Umsteiger, aber auch der Kern der Sprache. Jeder Wert hat genau einen Eigentümer. Wird der Eigentümer aus dem Scope geworfen, wird der Speicher freigegeben. Werte können entweder übertragen (move) oder geliehen (borrow) werden — geliehen entweder mutabel und exklusiv oder unverändert und mehrfach. Diese drei Regeln reichen aus, um eine ganze Klasse von Fehlern strukturell unmöglich zu machen.

Für ein Trading-System bedeutet das: Wenn ein Order-Buch in Thread A modifiziert wird, kann Thread B es nicht gleichzeitig lesen — der Compiler verbietet es. Möchte man das doch tun, muss man explizit eine synchronisierte Struktur (Arc<Mutex<T>>, RwLock, atomare Typen) verwenden. Damit ist die Synchronisationsfrage Teil der Typsignatur und nicht mehr versteckte Tribal Knowledge.

Ein kleines Beispiel: ein Tick-Aggregator.

Hier ein einfacher Aggregator, der ankommende Trades aufsummiert. In Rust ist die Threading-Semantik bereits aus der Signatur ablesbar:

use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;

pub struct TickAggregator {
    volume: AtomicU64,
    count: AtomicU64,
    started: Instant,
}

impl TickAggregator {
    pub fn new() -> Arc<Self> {
        Arc::new(Self {
            volume: AtomicU64::new(0),
            count: AtomicU64::new(0),
            started: Instant::now(),
        })
    }

    pub fn record(&self, qty: u64) {
        self.volume.fetch_add(qty, Ordering::Relaxed);
        self.count.fetch_add(1, Ordering::Relaxed);
    }

    pub fn snapshot(&self) -> (u64, u64, f64) {
        let v = self.volume.load(Ordering::Acquire);
        let c = self.count.load(Ordering::Acquire);
        let elapsed = self.started.elapsed().as_secs_f64();
        (v, c, v as f64 / elapsed.max(1e-9))
    }
}

Dieser Aggregator ist garantiert thread-safe, ohne dass ein einziger expliziter Lock nötig wäre. Mehrere Producer können record parallel aufrufen, ein Consumer kann jederzeit snapshot lesen. Die Atomics sind hardwarenahe Instruktionen ohne Sprung über Kernel-Calls.

Performance in der Praxis.

In Benchmarks liegt Rust bei vergleichbarem Code typischerweise auf Augenhöhe mit C++, gelegentlich darunter, gelegentlich darüber. Wichtig ist, dass beide Sprachen auf LLVM aufsetzen — die Backend-Optimierungen sind dieselben. Unterschiede entstehen vor allem durch Idiome: Rust-Iteratoren werden oft besser inlined als manuelle Schleifen, während C++-Templates manchmal aggressivere Spezialisierung erlauben.

Für Latenz-kritische Pfade gilt in Rust wie in C++: keine Allokationen im Hot Path, keine virtuellen Aufrufe, keine Boxing-Operationen. Die Sprache zwingt nicht zu Heap-Allokationen — Vec kann mit with_capacity vorab allokiert, SmallVec hält kleine Mengen auf dem Stack, und mit #![no_std] lassen sich ganze Module ohne Heap betreiben.

Ein FIX-Parser ohne Allokation.

Ein häufiger Engpass in Trading-Systemen ist das Parsen von Marktdaten oder Protokollen wie FIX. Eine zero-allocation Variante in Rust nutzt Slices auf einen Eingabe-Buffer:

#[derive(Debug)]
pub struct FixField<'a> {
    pub tag: u32,
    pub value: &'a [u8],
}

pub fn parse_fix<'a>(input: &'a [u8]) -> impl Iterator<Item = FixField<'a>> {
    input.split(|&b| b == 0x01).filter_map(|pair| {
        let eq = pair.iter().position(|&b| b == b'=')?;
        let tag = std::str::from_utf8(&pair[..eq]).ok()?.parse().ok()?;
        Some(FixField { tag, value: &pair[eq + 1..] })
    })
}

Die Lifetime 'a garantiert, dass die zurückgegebenen Slices nur so lange existieren wie der Eingabe-Buffer. Ein versehentliches Halten nach Buffer-Recycling würde nicht kompilieren — ein klassischer C-Bug, der hier strukturell ausgeschlossen ist. Solche Garantien sind in Performance-Code Gold wert.

Das Ökosystem: erwachsen geworden.

Vor fünf Jahren musste man in Rust noch viele Komponenten selbst bauen. Heute steht ein ausgewachsenes Ökosystem bereit: tokio für asynchrones I/O, nom oder winnow für Parser, serde für Serialisierung, polars für DataFrames, arrow-rs für Columnar-Daten, crossbeam für lock-freie Datenstrukturen, rayon für Data-Parallelism. Für Anbindungen an Broker existieren Crates wie fix-rs, binance-rs oder native Wrapper um die proprietären C-Bibliotheken der großen Anbieter.

Auch das Tooling ist hervorragend: cargo als Build-System und Paketmanager, clippy als Linter mit über 700 Regeln, rustfmt für Formatierung, miri für Detektion von Undefined Behavior. Wer aus dem C++-Lager mit CMake, vcpkg und einem halben Dutzend Linter kämpft, wird dieses Setup schätzen lernen.

Wo Rust noch nicht überzeugt.

Drei ehrliche Einschränkungen gehören dazu:

Keiner dieser Punkte ist ein Showstopper. Aber sie gehören in eine ehrliche Bewertung, wenn Sie überlegen, einen produktiven Stack umzustellen.

Mein Fazit für die Praxis.

Für neue Komponenten in einem Trading-Stack ist Rust 2026 die bessere Wahl als C++, wenn das Team bereit ist, vier bis sechs Wochen in die Lernkurve zu investieren. Bestehende C++-Systeme werden Sie nicht über Nacht ersetzen — und sollten es auch nicht. Aber das nächste Modul, der nächste Market-Data-Handler, der nächste Order-Router: dafür lohnt sich ein nüchterner Blick auf das, was Rust heute leistet.

Sie überlegen, ob Rust für Ihr Trading-System Sinn ergibt? Erstgespräch buchen — wir bewerten Aufwand, Nutzen und Migrationspfad gemeinsam.