Ist Rust schwer? Was in den ersten Wochen passiert
Die Syntax kostet dich Tage, das Eigentumsmodell kostet Wochen, und die Übersetzungszeiten kosten dich den gewohnten Rhythmus. Alle drei lassen sich einplanen, wenn man sie kennt.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Der Aufwand liegt nicht dort, wo ihn die meisten einplanen
Der übliche Plan lautet: zwei Tage Kurs, danach ins Projekt. Für die Syntax geht das auf. Es scheitert an der ersten Datenstruktur, die keine schlichte Liste ist, also an einem Zwischenspeicher, in den mehrere Stellen schreiben, an einem Baum, dessen Knoten auf ihren Elternknoten zeigen, oder an einem Konfigurationsobjekt, das überall herumgereicht wird. In jeder anderen Sprache sind das Aufgaben von zwanzig Minuten. In Rust ist es der Punkt, an dem du lernst, dass sich der Entwurf ändern muss und nicht der Code.
Die Frustrationskurve verläuft dabei nicht gerade. Die erste Woche fühlt sich gut an, die zweite fühlt sich an, als arbeite die Sprache gegen dich, und die Phase, in der du denselben Fehler zum fünften Mal nachschlägst, entscheidet darüber, ob jemand dranbleibt oder anfängt, überall clone hinzuschreiben, damit Ruhe ist. Klonen ist an vielen Stellen legitim, als Reflex erzeugt es aber Code, der langsam ist und gleichzeitig verschweigt, wie die Daten eigentlich zusammenhängen.
Teuer wird das, wenn ein Team das erste Rust-Projekt schätzt wie ein Go- oder Java-Projekt. Nach drei Wochen hält die Schätzung für die Teile, die einem Lehrbuchbeispiel ähneln, und bricht bei den Teilen, die das nicht tun. Danach hat Rust im Haus einen Ruf, und dieser Ruf ist deutlich schwerer zu reparieren als der Code.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Die fünf Sackgassen, in denen der Einstieg üblicherweise stecken bleibt
Was hier steht, sind keine Anfängerfehler im Sinne von Unwissen, sondern die Stellen, an denen Gewohnheiten aus anderen Sprachen zuverlässig gegen das Eigentumsmodell laufen. Wer sie vorher kennt, verliert dort Stunden statt Tage.
Symptom
Nach zwei Wochen kompiliert alles, aber der Code steckt voller clone und to_string, und die Rust-Fassung ist langsamer als die Python-Fassung davor.
Ursache
Klonen ist der schnellste Weg, den Compiler ruhigzustellen, und deshalb der erste Reflex. Es beseitigt das Symptom und verschiebt die Frage, wem die Daten gehören, statt sie zu beantworten.
Lösung
Geh die Klone einzeln durch und frag pro Stelle: Braucht diese Funktion die Daten wirklich, oder reicht ihr ein Blick darauf? Meistens genügt eine Referenz als Parameter, oft reicht &str statt String und ein Ausschnitt statt eines eigenen Vec. Klone beim Laden von Konfiguration und beim Aufbau lässt du stehen, die kosten nichts.
Symptom
Jede Änderung braucht Minuten bis zur Rückmeldung, und der gewohnte Rhythmus aus Ändern und Ausprobieren ist weg.
Ursache
Rust übersetzt Generics für jeden verwendeten Typ neu und optimiert aggressiv. Dazu kommt der Bindeschritt am Ende, der bei einem großen Programm einen erheblichen Teil der Zeit ausmacht.
Lösung
Beim Schreiben cargo check statt cargo build nutzen, das prüft Typen und Ausleihen ohne fertiges Programm. Das Projekt in einen Workspace aus mehreren Crates aufteilen, damit eine Änderung nicht alles neu übersetzt. Für den Bindeschritt einen schnelleren Linker eintragen. Und Tests so schneiden, dass nicht jeder Durchlauf das ganze Programm braucht.
Symptom
Ein Modell, das in Java in zwanzig Minuten stand, lässt sich nicht übertragen: Knoten mit Verweis auf den Elternknoten, ein Zwischenspeicher, in den mehrere Teile schreiben, ein Objekt, das überall herumgereicht wird.
Ursache
Das sind Geflechte aus Zeigern, die in einer Sprache mit Speicherbereinigung nichts kosten. Rust verlangt an jeder Kante eine Aussage darüber, wer den Wert besitzt und wie lange er lebt, und ein Geflecht hat darauf keine eindeutige Antwort.
Lösung
Zeiger durch Indizes ersetzen: Alle Knoten liegen in einem Vec, Kanten sind Zahlen statt Referenzen. Für geteilten Besitz innerhalb eines Threads Rc, mit veränderlichem Inhalt Rc mit RefCell, über Threads hinweg Arc mit Mutex. Wichtig ist die Reihenfolge: erst das Modell umbauen, erst danach zu diesen Typen greifen.
Symptom
Der Compiler meldet, ein Wert sei verschoben worden, obwohl im Quelltext nirgends etwas von Verschieben steht.
Ursache
Beim Zuweisen oder Übergeben eines Werts, der keine Kopie erlaubt, geht der Besitz an die neue Stelle über, und die alte Variable ist danach nicht mehr benutzbar. Bei Zahlen und anderen kleinen Typen passiert das nicht, deshalb fällt es erst bei String, Vec oder eigenen Strukturen auf. Ein häufiger Auslöser ist eine for-Schleife, die direkt über die Sammlung läuft statt über &sammlung, denn die verbraucht die Sammlung.
Lösung
Entscheide pro Aufruf, ob die Funktion den Wert wirklich übernehmen soll. Wenn nicht, nimm eine Referenz als Parameter und ändere die Signatur entsprechend. Wenn doch und du brauchst den Wert danach noch, ist an dieser Stelle ein Klon die richtige Antwort und keine Notlösung.
Symptom
Der Kurs war gut, aber drei Wochen später sitzt nichts mehr davon, weil im Projekt weiter C# oder Java ansteht.
Ursache
Rust behält man nicht nebenher. Ohne eine Aufgabe, an der die Regeln regelmäßig weh tun, verblasst genau der Teil, der neu war, und übrig bleibt die Syntax, die ohnehin niemandem Probleme macht.
Lösung
Direkt nach dem Kurs eine kleine, aber echte Aufgabe belegen, die in vier bis sechs Wochen fertig wird und danach tatsächlich benutzt wird. Ein internes Kommandozeilenwerkzeug eignet sich dafür besser als ein Prototyp, den anschließend niemand betreibt.
Der Einstieg in fünf Stufen
- 01 Tage eins bis drei: Syntax, cargo und ein Programm, das etwas Sinnvolles ausgibt.
- 02 Woche eins: Datentypen, Option und Result, erste Fehlerbehandlung ohne unwrap.
- 03 Woche zwei: Der Borrow-Checker meldet sich, sobald Daten mehrere Stellen berühren.
- 04 Woche drei und vier: Eigene Strukturen entwerfen, statt gegen die Regeln zu schreiben.
- 05 Ab Woche fünf: Async, Traits und Generics, am besten anhand einer echten Aufgabe.
Was den Einstieg tatsächlich verkürzt
Der Unterschied zwischen vier und zwölf Wochen liegt selten an der Begabung, sondern an der Reihenfolge der Aufgaben, am Umgang mit den Fehlermeldungen und daran, ob jemand da ist, der dieselben Sackgassen schon durchlaufen hat.
Erst lesen und umformen, dann entwerfen
Fang mit Code an, der einer Pipeline folgt: Eingabe lesen, umformen, ausgeben. Dort ist das Eigentumsmodell fast unsichtbar, weil die Daten in eine Richtung fließen. Strukturen, in denen etwas auf etwas anderes zurückzeigt, verschiebst du bewusst nach hinten.
Die Fehlermeldung zu Ende lesen
Der Compiler nennt die Stelle, den Grund und oft einen konkreten Vorschlag, dazu einen Fehlercode. Mit rustc --explain und diesem Code bekommst du die lange Erklärung samt Beispiel. Das ist das eigentliche Lehrbuch, und die meisten überspringen es zugunsten der ersten Forensuche.
Klonen bewusst setzen, nicht reflexhaft
Ein clone beim Laden der Konfiguration kostet nichts und ist die richtige Antwort. Dasselbe clone in einer Schleife über eine Million Datensätze ist der Grund, warum die Rust-Fassung am Ende langsamer ist als die Python-Fassung davor. Entscheide mit Größe und Häufigkeit vor Augen.
Den Rhythmus auf cargo check umstellen
Cargo check prüft Typen und Ausleihen, ohne ein Programm zu erzeugen, und ist deutlich schneller als ein vollständiger Bau. Der Editor macht über rust-analyzer dasselbe fortlaufend. Cargo build gehört an die Stelle, an der du tatsächlich etwas ausführen willst.
Die Übersetzungszeit als Projektentscheidung behandeln
Eine Aufteilung in einen Workspace aus mehreren Crates sorgt dafür, dass eine Änderung nur ihr eigenes Paket und dessen Abhängige neu übersetzt. Für den Bindeschritt lässt sich ein schnellerer Linker eintragen. Beides ist einmalige Arbeit und wirkt danach jeden Tag.
Die erste Aufgabe klein und echt wählen
Ein Kommandozeilenwerkzeug, das im Team wirklich gebraucht wird, etwa ein Auswerter für Protokolldateien oder ein Migrationsskript. Klein genug, um fertig zu werden, echt genug, dass die Arbeit danach nicht weggeworfen wird.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Was aus welcher Sprache leicht und was schwer fällt
Aus Java oder C# kommend liest sich die Syntax vertraut. Die erste echte Umstellung ist die Fehlerbehandlung: Es gibt kein throw, das zehn Ebenen überspringt, der Fehler reist als Rückgabewert und wird mit dem Fragezeichen-Operator weitergereicht. Die eigentliche Hürde kommt danach, weil der Objektgraph, den die Speicherbereinigung bisher umsonst getragen hat, plötzlich eine Entwurfsentscheidung ist. Schnittstellen entsprechen ungefähr Traits, funktionieren aber anders, weil sich eine Implementierung auch außerhalb des Typs unterbringen lässt.
Aus Python kommend sind Typen überall, ein Übersetzungsschritt kommt dazu, und die Gewohnheit, kurz ein Wörterbuch zwischenzuschalten, funktioniert nicht mehr. Positiv fällt zweierlei auf: Cargo ist gegenüber der Paketlage in Python eine Erleichterung, und der Compiler fängt vieles ab, wofür bisher Tests zuständig waren.
Aus C++ kommend ist der Start am leichtesten und die Mitte am ärgerlichsten. RAII, Verschiebesemantik und Templates haben Entsprechungen, aber der Borrow-Checker setzt durch, was in C++ Konvention war. Die typische Reibung: Muster, die dort völlig üblich sind, etwa ein roher Zeiger in einen Container hinein oder eine Referenz auf ein Feld eines anderen Objekts, werden schlicht abgelehnt. Aus Go kommend ist die Fehlerbehandlung vertraut, async und Generics sind es nicht, und die Bauzeiten fallen unangenehm auf.
Die drei Stellen, an denen fast alle hängen bleiben
Die erste ist das Verschieben. Wenn du einen String an eine Funktion übergibst, geht der Besitz über, und die ursprüngliche Variable ist danach tot. Das ist die erste Überraschung, weil im Quelltext nichts davon steht und weil es bei Zahlen nicht passiert. Wer einmal verstanden hat, dass die Sprache zwischen Typen unterscheidet, die sich billig kopieren lassen, und solchen, die einen Besitzer haben, erkennt die Meldung ab dann sofort.
Die zweite ist das Ausleihen. Solange eine lesende Ausleihe lebt, gibt es keine veränderliche, und umgekehrt. Der klassische Auslöser ist eine Schleife über eine Sammlung, in deren Rumpf du dieselbe Sammlung änderst. Die Regel verhindert genau das, was in C++ zu ungültigen Iteratoren führt, wenn ein Vektor beim Wachsen umzieht, und dieser Zusammenhang macht sie für viele erst einleuchtend.
Die dritte sind Lebensdauern in Strukturen. In dem Moment, in dem du eine Referenz in ein Feld schreibst, will der Compiler eine Angabe dazu, und diese Angabe zieht sich anschließend durch jede Funktion und jeden Trait, in dem der Typ vorkommt. Die richtige Antwort ist für Einsteiger fast immer: keine Referenz speichern, sondern den Wert selbst. Ein String im Feld statt eines Ausschnitts kostet eine Speicheranforderung und erspart dir eine ganze Kategorie von Folgeproblemen.
Die Übersetzungszeit und was sie mit dem Lernen macht
Rust baut langsam, und das hat nachvollziehbare Gründe. Generischer Code wird für jeden konkreten Typ neu übersetzt, der Optimierer arbeitet gründlich, und am Ende steht der Bindeschritt, der bei einem großen Programm einen erheblichen Anteil ausmacht. Bei einem Projekt mit vielen Abhängigkeiten dauert der erste vollständige Bau entsprechend, und wer das nicht erwartet, hält es für einen Konfigurationsfehler.
Für das Lernen ist das ein echtes Hindernis, weil die Schleife aus Ändern, Ausführen und Hinsehen, die in Python funktioniert, hier nicht funktioniert. Der tragfähige Ersatz ist ein anderer Rhythmus: Der Editor prüft über rust-analyzer fortlaufend im Hintergrund, cargo check liefert im Terminal in Sekunden die Typ- und Ausleihprüfung, und ein vollständiger Bau steht erst an, wenn du wirklich etwas ausführen willst. Das klingt nach einer Kleinigkeit und entscheidet in der Praxis darüber, wie viele Versuche pro Stunde möglich sind.
Im Projekt helfen drei Dinge dauerhaft: die Aufteilung in einen Workspace, damit eine Änderung nur ihr eigenes Paket neu übersetzt, ein schlanker Abhängigkeitsbaum, weil jede Abhängigkeit mit übersetzt wird, und ein schnellerer Linker für den Bindeschritt. Ganz weg geht das Problem nicht. Es wird beherrschbar, und diese Unterscheidung sollte in jeder Planung stehen.
Wie ein Team den Einstieg abfedert, ohne den Termin zu verlieren
Nicht mit dem kritischen Bauteil anfangen. Das erste Rust-Stück sollte abgegrenzt sein und im Zweifel weggeworfen werden können. Und nie mit einer einzigen Person: Wer allein lernt, hat niemanden für ein Review, und der gesamte Code hängt danach an einem Kalender. Zu zweit kommt ihr spürbar schneller voran als eine Person allein, weil die meisten Borrow-Checker-Fragen Entwurfsfragen sind und sich im Gespräch in Minuten klären.
Die Konventionen gehören früh aufgeschrieben, und zwar bevor der erste Code entsteht: welche Bibliothek für Fehlertypen, welche Laufzeitumgebung für async, wann ein Klon in Ordnung ist, wann eine Funktion den Besitz übernimmt und wann sie ausleiht. Ohne diese Festlegungen beantwortet jedes Modul die Fragen anders, und die Codebasis wird schwer lesbar, lange bevor sie groß ist.
Zum Zeitpunkt eines Kurses gibt es eine Faustregel, die sich bewährt hat: Er wirkt besser nach zwei Wochen eigener Versuche als davor. Wer schon einmal gegen die Wand gelaufen ist, stellt andere Fragen und nimmt Antworten mit, für die es vorher keinen Anlass gab. Und die Aufgabe, an der danach weitergearbeitet wird, sollte vor dem Kurs feststehen, sonst verfällt der Stoff, bevor er zum Einsatz kommt.
Dazu passende Kurse
Weil erfahrungsgemäß die zweite Woche die unangenehme ist, spart ein begleiteter Einstieg in Rust genau dort die Tage, die sonst in der Fehlersuche versickern.
Wer bisher nie mit Stapel und Halde zu tun hatte, tut sich mit den Eigentumsregeln erkennbar schwerer, und dafür lohnt es sich, vorher Grundlagen der Softwareentwicklung aufzufrischen.
Wie sicher bist du beim Thema wirklich?
Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.
Wo du genau das übst
Sehr angenehmes, gut vorbereitetes Seminar, kann ich nur empfehlen.
Häufige Fragen
Wie lange dauert es bis zur ersten produktiven Aufgabe?
Reicht ein zweitägiger Kurs?
Kann jemand ohne C oder C++ Rust lernen?
Was ist schwerer, der Borrow-Checker oder async?
Deine Ansprechpartner
Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.
Yves Hoppe
Weiterbildung & Beratung
Hilft dir, aus dem Rust-Programm den passenden Kurs für deinen Stand zu finden.
Norbert Jansen
Beratung & Inhouse
Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.
Die ersten Wochen mit jemandem im Raum
Bei cmt machst du den Einstieg mit einem Trainer, der die üblichen Sackgassen kennt und dir sagt, ob dein Entwurf ein Rust-Problem hat oder einen Denkfehler enthält.