<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rust on kenji.blog</title><link>http://kenji.blog/de/categories/rust/</link><description>Recent content in Rust on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 04:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/categories/rust/index.xml" rel="self" type="application/rss+xml"/><item><title>Erklärung von 'Ownership' und 'Borrowing' in Rust im Vergleich zu C++ Zeigern</title><link>http://kenji.blog/de/p/rust-ownership-borrowing-cpp-pointer-comparison/</link><pubDate>Sat, 12 Sep 2026 04:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/rust-ownership-borrowing-cpp-pointer-comparison/</guid><description>&lt;img src="http://kenji.blog/p/rust-ownership-borrowing-cpp-pointer-comparison/img/eyecatch.jpg" alt="Featured image of post Erklärung von 'Ownership' und 'Borrowing' in Rust im Vergleich zu C++ Zeigern" />&lt;p>In der modernen Systemprogrammierung ist die Vereinbarkeit von Leistung und Speichersicherheit eine ständige Herausforderung. C++ war lange Zeit der unangefochtene König in diesem Bereich, aber in den letzten Jahren hat Rust begonnen, diese Position zu bedrohen. Das größte Merkmal von Rust ist die Garantie der Speichersicherheit zur Kompilierzeit ohne Garbage Collection (GC), was durch die Konzepte von &amp;ldquo;Ownership&amp;rdquo; (Eigentum) und &amp;ldquo;Borrowing&amp;rdquo; (Ausleihen) erreicht wird.&lt;/p>
&lt;p>In diesem Artikel werden wir C++ Zeiger (rohe Zeiger, &lt;code>std::unique_ptr&lt;/code>, &lt;code>std::shared_ptr&lt;/code>) und das Rust-Ownership-Modell detailliert vergleichen. Wir werden anhand von Codebeispielen und Diagrammen ausführlich erklären, wie der Rust-Compiler (Borrow Checker) Use-After-Free (Verwendung nach Freigabe) und Datenrennen (Data Races) verhindert.&lt;/p>
&lt;h2 id="1-grundlagen-der-speicherverwaltung-stack-und-heap">1. Grundlagen der Speicherverwaltung: Stack und Heap
&lt;/h2>&lt;p>Um die Grundlagen der Speicherverwaltung zu verstehen, lassen Sie uns zunächst einen Blick darauf werfen, wie Programme den Speicher nutzen. Der Speicherbereich wird grob in &amp;ldquo;Stack&amp;rdquo; und &amp;ldquo;Heap&amp;rdquo; unterteilt.&lt;/p>
&lt;h3 id="stack">Stack
&lt;/h3>&lt;p>Dies ist der Bereich, in dem lokale Variablen bei Funktionsaufrufen abgelegt werden. Er hat eine LIFO-Struktur (Last-In-First-Out) und die Speicherzuweisung und -freigabe ist sehr schnell. Hier werden nur Daten platziert, deren Größe zur Kompilierzeit bestimmt werden kann.&lt;/p>
&lt;h3 id="heap">Heap
&lt;/h3>&lt;p>Hier werden Daten platziert, deren Größe zur Laufzeit dynamisch bestimmt wird, oder Daten, die über den Gültigkeitsbereich einer Funktion hinaus existieren müssen. Auf sie wird über Zeiger (oder Referenzen) zugegriffen.&lt;/p>
&lt;p>In C++ und Rust, die keine Garbage Collection haben, kann der Verwaltungsaufwand für den Heapspeicher als Formel wie folgt modelliert werden. Wenn die Gesamtzahl der Objekte $N$ ist, die durchschnittliche Zeit für die Allokation $T_{alloc}$ und die durchschnittliche Zeit für die Deallokation $T_{dealloc}$ ist, dann ist der Gesamtaufwand für die Speicherverwaltung $C_{memory}$:&lt;/p>
$$ C_{memory} = \sum_{i=1}^{N} (T_{alloc, i} + T_{dealloc, i}) + O_{sync} $$
&lt;p>Hierbei ist $O_{sync}$ der Overhead für die gegenseitige Ausschließung (Mutex- oder atomare Operationen) in einer Multithread-Umgebung. Da Rust den Zeitpunkt der Speicherfreigabe zur Kompilierzeit bestimmt, führt es $T_{dealloc}$ zu einem zuverlässigen und sicheren Zeitpunkt aus, während die Durchsatzminderung (Stop-The-World) durch die Garbage Collection zur Laufzeit auf null reduziert wird.&lt;/p>
&lt;div class="mermaid">graph TD
A["Programmspeicher"] --> B["Stack (Schnell, Feste Größe)"]
A --> C["Heap (Dynamisch, Langsamer)"]
B --> D["Lokale Variablen"]
B --> E["Zeiger/Referenzen"]
C --> F["Dynamisch zugewiesene Daten"]
E -.->|"Zeigt auf"| F&lt;/div>
&lt;h2 id="2-c-zeiger-der-kompromiss-zwischen-freiheit-und-gefahr">2. C++ Zeiger: Der Kompromiss zwischen Freiheit und Gefahr
&lt;/h2>&lt;p>Werfen wir einen Blick auf die Entwicklung der Speicherverwaltung in C++.&lt;/p>
&lt;h3 id="die-ära-der-rohen-zeiger-raw-pointers-und-ihre-probleme">Die Ära der rohen Zeiger (Raw Pointers) und ihre Probleme
&lt;/h3>&lt;p>Rohe Zeiger (&lt;code>*&lt;/code>), die von der Sprache C übernommen wurden, bieten ultimative Freiheit, sind aber gleichzeitig eine Brutstätte für die folgenden schwerwiegenden Bugs:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Speicherleck (Memory Leak)&lt;/strong>: Vergessen, mit &lt;code>new&lt;/code> zugewiesenen Speicher mit &lt;code>delete&lt;/code> freizugeben.&lt;/li>
&lt;li>&lt;strong>Hängender Zeiger (Dangling Pointer)&lt;/strong>: Zugriff auf einen Zeiger nach der Speicherfreigabe (nach &lt;code>delete&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Doppelte Freigabe (Double Free)&lt;/strong>: Zweimaliges Aufrufen von &lt;code>delete&lt;/code> für denselben Speicherbereich.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// C++: Beispiel für Probleme mit rohen Zeigern
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="nf">rawPointerExample&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... Irgendeine Verarbeitung ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">delete&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Versehentlicher erneuter Zugriff (Use-After-Free / Dangling Pointer)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Der C++-Compiler kann dies nicht als Kompilierfehler erkennen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ptr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Undefiniertes Verhalten (Undefined Behavior)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="die-einführung-von-raii-und-smart-pointern-ab-c11">Die Einführung von RAII und Smart Pointern (ab C++11)
&lt;/h3>&lt;p>Seit C++11 wurden Smart Pointer, die auf dem RAII-Konzept (Resource Acquisition Is Initialization) basieren, standardisiert, und die direkte Verwendung von rohen Zeigern wird nicht mehr empfohlen.&lt;/p>
&lt;h4 id="stdunique_ptr">&lt;code>std::unique_ptr&lt;/code>
&lt;/h4>&lt;p>Ein Zeiger, der exklusives Eigentum (Ownership) ausdrückt. Der Speicher wird automatisch freigegeben, wenn der Gültigkeitsbereich (Scope) verlassen wird. Er kann nicht kopiert werden; nur das &amp;ldquo;Verschieben&amp;rdquo; (Move) des Eigentums ist möglich (unter Verwendung von &lt;code>std::move&lt;/code>).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// C++: std::unique_ptr
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">uniquePtrExample&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">p1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">42&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// std::unique_ptr&amp;lt;int&amp;gt; p2 = p1; // Kompilierfehler (Kopieren nicht möglich)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">p3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p1&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Verschieben des Eigentums
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Schwäche von C++: Nach dem Move wird p1 zu nullptr, aber der Zugriff selbst ist kompilierbar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Führt zur Laufzeit zu einem Absturz (Segmentation Fault)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// std::cout &amp;lt;&amp;lt; *p1 &amp;lt;&amp;lt; std::endl;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="stdshared_ptr">&lt;code>std::shared_ptr&lt;/code>
&lt;/h4>&lt;p>Ein Zeiger, der es mehreren Zeigern ermöglicht, dasselbe Objekt zu teilen. Er verwendet Referenzzählung (Reference Counting), um den Speicher freizugeben, wenn der Zähler 0 erreicht. Da atomare Inkrementierungs- und Dekrementierungsoperationen erforderlich sind, entsteht ein gewisser Leistungs-Overhead (entsprechend dem oben genannten $O_{sync}$).&lt;/p>
&lt;h2 id="3-rusts-ownership-eigentum-ein-paradigmenwechsel">3. Rusts Ownership (Eigentum): Ein Paradigmenwechsel
&lt;/h2>&lt;p>Rust hat das Konzept von &lt;code>std::unique_ptr&lt;/code> aus C++ als Kern seiner Sprachspezifikation übernommen und verfügt über ein noch strengeres &amp;ldquo;Ownership-Modell&amp;rdquo;.&lt;/p>
&lt;h3 id="die-3-regeln-von-ownership">Die 3 Regeln von Ownership
&lt;/h3>&lt;p>Das Ownership-System von Rust basiert auf den folgenden drei sehr einfachen Regeln:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Jeder Wert in Rust hat eine Variable, die als sein Eigentümer (Owner) bezeichnet wird.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Es kann immer nur einen Eigentümer zur gleichen Zeit geben.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Wenn der Eigentümer den Gültigkeitsbereich (Scope) verlässt, wird der Wert verworfen.&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>In Rust werden Ressourcen standardmäßig &amp;ldquo;verschoben&amp;rdquo; (moved). Das Eigentum wird durch Zuweisungsoperationen verschoben, ohne dass &lt;code>std::move&lt;/code> wie in C++ explizit angegeben werden muss.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Rust: Verschieben des Eigentums (Move)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">String&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Daten, die auf dem Heap allokiert werden
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Das Eigentum wird von s1 auf s2 verschoben (moved)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Der größte Unterschied zu C++: Der Zugriff auf eine Variable nach dem Move führt zu einem &amp;#34;Kompilierfehler&amp;#34;!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// println!(&amp;#34;{}, world!&amp;#34;, s1); // Kompilierfehler: value borrowed here after move
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Diese Funktion, den Zugriff auf Variablen nach einem Move zur Kompilierzeit zu verhindern, ist einer der Gründe, warum Rust sicherer ist als &lt;code>std::unique_ptr&lt;/code> in C++.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant S1 as "Variable s1"
participant Heap as "Heap-Speicher ('hello')"
participant S2 as "Variable s2"
S1->>Heap: "Weist zu &amp; Besitzt"
Note over S1,S2: "let s2 = s1;"
S1--xHeap: "Verliert Eigentum (Ungültig)"
S2->>Heap: "Übernimmt Eigentum"&lt;/div>
&lt;h2 id="4-borrowing-ausleihen-und-referenzen">4. Borrowing (Ausleihen) und Referenzen
&lt;/h2>&lt;p>Wenn man das Eigentum ständig verschiebt, müsste man es jedes Mal zurückgeben lassen, wenn man einen Wert an eine Funktion übergibt, was sehr unpraktisch ist. Hier kommt das &amp;ldquo;Borrowing&amp;rdquo; (Ausleihen) ins Spiel. Es entspricht Zeigern oder Referenzen in C++.&lt;/p>
&lt;p>In Rust gibt es zwei Arten von Borrowing:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Unveränderliche Referenz (Immutable Reference)&lt;/strong>: &lt;code>&amp;amp;T&lt;/code> (ähnlich wie &lt;code>const T&amp;amp;&lt;/code> in C++)&lt;/li>
&lt;li>&lt;strong>Veränderliche Referenz (Mutable Reference)&lt;/strong>: &lt;code>&amp;amp;mut T&lt;/code> (ähnlich wie &lt;code>T&amp;amp;&lt;/code> in C++)&lt;/li>
&lt;/ul>
&lt;h3 id="die-unerbittlichen-regeln-des-borrow-checkers">Die unerbittlichen Regeln des Borrow Checkers
&lt;/h3>&lt;p>Der Rust-Compiler enthält einen &amp;ldquo;Borrow Checker&amp;rdquo;, der die Gültigkeit von Referenzen überprüft. Der Borrow Checker erzwingt die folgenden strengen Regeln:&lt;/p>
&lt;blockquote>
&lt;p>In jedem beliebigen Gültigkeitsbereich kann nur eine der folgenden Bedingungen existieren:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Genau eine veränderliche Referenz (&lt;code>&amp;amp;mut T&lt;/code>)&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Mehrere unveränderliche Referenzen (&lt;code>&amp;amp;T&lt;/code>)&lt;/strong>&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>Dies wird als das Prinzip &lt;strong>&amp;ldquo;Multiple Readers XOR Single Writer (MRSW)&amp;rdquo;&lt;/strong> bezeichnet. Es kann durch ein mathematisches exklusives ODER (XOR) ausgedrückt werden. Für einen Zustand $S$ müssen die Anzahl der unveränderlichen Referenzen $N_r$ und die Anzahl der veränderlichen Referenzen $N_w$ die folgende Einschränkung erfüllen:&lt;/p>
$$ (N_r \ge 0 \land N_w = 0) \oplus (N_r = 0 \land N_w = 1) $$
&lt;p>Diese Regel eliminiert &lt;strong>Datenrennen (Data Races) zur Kompilierzeit vollständig&lt;/strong>. Ein Datenrennen tritt auf, wenn ① zwei oder mehr Zeiger gleichzeitig auf dieselben Daten zugreifen, ② mindestens einer davon schreibt und ③ kein Synchronisationsmechanismus vorhanden ist. Rust verhindert Datenrennen im Vorfeld, indem es die Bedingung ② zur Kompilierzeit bricht.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Rust: Kompilierfehler durch Verletzung der Borrowing-Regeln
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">String&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">r1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Unveränderliches Ausleihen (OK)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">r2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Unveränderliches Ausleihen (OK)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// let r3 = &amp;amp;mut s; // Fehler! Es kann keine veränderliche Referenz erstellt werden, solange unveränderliche Referenzen existieren
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">, &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">r1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">r2&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-verhinderung-der-iterator-ungültigmachung-iterator-invalidation">5. Verhinderung der Iterator-Ungültigmachung (Iterator Invalidation)
&lt;/h2>&lt;p>Als konkretes Beispiel, bei dem die Leistungsfähigkeit des Borrow Checkers am deutlichsten wird, betrachten wir den klassischen Bug der &amp;ldquo;Iterator-Ungültigmachung&amp;rdquo;.&lt;/p>
&lt;h3 id="iterator-ungültigmachung-in-c-laufzeitabsturz">Iterator-Ungültigmachung in C++ (Laufzeitabsturz)
&lt;/h3>&lt;p>Wenn ein &lt;code>std::vector&lt;/code> in C++ innerhalb einer Schleife geändert wird, besteht die Möglichkeit, dass der dahinterliegende Speicher neu zugewiesen (reallocated) wird, wodurch Referenzen zu hängenden Zeigern (Dangling Pointers) werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// C++: Bug der Iterator-Ungültigmachung
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">v&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Referenz auf das Element des Vektors abrufen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">first&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Element hinzufügen (Wenn die Kapazität hier nicht ausreicht, wird ein neuer Speicherbereich zugewiesen,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// und der alte Bereich könnte verworfen werden)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">v&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push_back&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// first könnte auf bereits freigegebenen Speicher zeigen! (Undefiniertes Verhalten)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;The first element is: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">first&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="schutz-zur-kompilierzeit-durch-rust">Schutz zur Kompilierzeit durch Rust
&lt;/h3>&lt;p>Lassen Sie uns genau dieselbe Logik in Rust schreiben.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Rust: Iterator-Ungültigmachung zur Kompilierzeit verhindern
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">vec!&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">];&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Unveränderliche Referenz abrufen (Beginn des Ausleihens)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">v&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Fehler! Solange `first` `v` unveränderlich ausleiht,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// kann die für `v.push` erforderliche veränderliche Referenz nicht erstellt werden.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// v.push(4);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;The first element is: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Da Rust auf Compiler-Ebene verbietet, &amp;ldquo;einen Wert zu ändern (veränderlich auszuleihen), während er gelesen wird (unveränderlich ausgeliehen ist)&amp;rdquo;, werden fatale Bugs wie Use-After-Free oder Iterator-Ungültigmachung zur Kompilierzeit zuverlässig abgefangen.&lt;/p>
&lt;div class="mermaid">graph LR
A["Variable v (Eigentümer)"] --> B["Heap Array [1, 2, 3]"]
C["Referenz 'first' (&amp;v[0])"] -.->|"Unveränderliches Ausleihen"| B
A -->|X "Veränderliches Ausleihen verweigert!"| D["v.push(4)"]
style C stroke:#00FF00,stroke-width:2px
style D stroke:#FF0000,stroke-width:2px&lt;/div>
&lt;h2 id="6-geteiltes-eigentum-shared-ownership-in-rust-rc-und-arc">6. Geteiltes Eigentum (Shared Ownership) in Rust: &lt;code>Rc&lt;/code> und &lt;code>Arc&lt;/code>
&lt;/h2>&lt;p>Geteiltes Eigentum, das dem &lt;code>std::shared_ptr&lt;/code> in C++ entspricht, ist auch in Rust verfügbar, jedoch gibt es eine klare Typentrennung für Single-Threaded- und Multi-Threaded-Anwendungen.&lt;/p>
&lt;h3 id="für-single-thread-rct-reference-counted">Für Single-Thread: &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> (Reference Counted)
&lt;/h3>&lt;p>&lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> ist ein nicht-thread-sicherer Smart Pointer mit Referenzzählung. Da er Inkrementierungs- und Dekrementierungsoperationen ohne atomare Befehle durchführt, ist er innerhalb eines einzelnen Threads sehr schnell. Wenn Sie jedoch versuchen, ihn an einen anderen Thread zu senden, führt dies zu einem Kompilierfehler (da er das &lt;code>Send&lt;/code>-Trait nicht implementiert).&lt;/p>
&lt;h3 id="für-multi-thread-arct-atomic-reference-counted">Für Multi-Thread: &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code> (Atomic Reference Counted)
&lt;/h3>&lt;p>Wenn Daten zwischen Threads geteilt werden sollen, wird &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code> verwendet, das atomare Inkrementierungen und Dekrementierungen durchführt. Es verursacht vergleichbare Kosten wie &lt;code>std::shared_ptr&lt;/code> in C++.&lt;/p>
&lt;p>Darüber hinaus tritt in C++ ein Datenrennen auf, wenn mehrere Threads gleichzeitig in eine von &lt;code>std::shared_ptr&lt;/code> gemeinsam genutzte Variable schreiben. Um dies zu verhindern, muss &lt;code>std::mutex&lt;/code> manuell und korrekt verwendet werden.&lt;/p>
&lt;p>Im Gegensatz dazu kann in Rust &lt;strong>die innere Struktur von &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code> allein nicht geändert werden&lt;/strong>. Wenn Änderungen erforderlich sind, muss es mit einem Mutex, d. h. &lt;code>Mutex&amp;lt;T&amp;gt;&lt;/code>, kombiniert werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">sync&lt;/span>::&lt;span class="p">{&lt;/span>&lt;span class="n">Arc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Mutex&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">thread&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Kombination aus thread-sicherem Teilen und gegenseitigem Ausschluss
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Ähnlich wie std::shared_ptr&amp;lt;std::mutex&amp;gt; in C++, aber der Mutex kapselt die Daten
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">counter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Arc&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Mutex&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handles&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">vec!&lt;/span>&lt;span class="p">[];&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">counter_clone&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Arc&lt;/span>::&lt;span class="n">clone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">counter&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">move&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Erst durch den Aufruf von lock() erhält man die veränderliche Referenz (&amp;amp;mut i32) auf das Innere
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">num&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">counter_clone&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">num&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Die Freigabe der Sperre (Lock) erfolgt automatisch durch RAII beim Verlassen des Gültigkeitsbereichs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handles&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handles&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Result: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">counter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Bemerkenswert ist, dass &lt;code>Mutex&amp;lt;T&amp;gt;&lt;/code> in Rust nicht nur ein einfacher Sperrmechanismus ist, sondern &lt;strong>&amp;ldquo;die zu schützenden Daten als Typ kapselt&amp;rdquo;&lt;/strong>. Dies verhindert auf Compiler-Ebene vollständig den Fehler, &amp;ldquo;auf Daten zuzugreifen und dabei zu vergessen, die Sperre zu erlangen&amp;rdquo;. Es ist so konzipiert, dass man ohne das Erlangen der Sperre (&lt;code>lock()&lt;/code>) keine Zugriffsrechte (Referenz) auf die darin enthaltenen Daten erhält.&lt;/p>
&lt;h2 id="fazit-vorabprüfung-durch-den-compiler-oder-eigenverantwortung-durch-den-entwickler">Fazit: &amp;ldquo;Vorabprüfung&amp;rdquo; durch den Compiler oder &amp;ldquo;Eigenverantwortung&amp;rdquo; durch den Entwickler
&lt;/h2>&lt;p>Zeiger und Smart Pointer in C++ bieten Entwicklern ein hohes Maß an Kontrolle und Leistung, aber ihre korrekte Verwendung hängt von der Disziplin des Entwicklers ab. Obwohl C++ durch die Einführung von RAII und &lt;code>std::unique_ptr&lt;/code> drastisch sicherer geworden ist, kann es &amp;ldquo;undefiniertes Verhalten&amp;rdquo; wie den Zugriff nach einem Move oder die Iterator-Ungültigmachung auf Sprachebene nicht vollständig verhindern.&lt;/p>
&lt;p>Auf der anderen Seite erkennt Rust diese Fehler zur &lt;strong>Kompilierzeit&lt;/strong> anstatt zur Laufzeit, indem es die Regeln von &amp;ldquo;Ownership&amp;rdquo; und &amp;ldquo;Borrowing&amp;rdquo; in den Compiler integriert. Die starke Garantie, dass &amp;ldquo;wenn es kompiliert, speichersicher ist&amp;rdquo;, ist der Hauptgrund, warum Rust in der Systemprogrammierung schnell an Popularität gewinnt.&lt;/p>
&lt;p>Der Kampf mit dem Borrow Checker von Rust (&amp;ldquo;Fighting the borrow checker&amp;rdquo;) ist für Anfänger eine große Hürde, aber letztendlich übernimmt der Compiler nur streng die komplexe Berechnung der &amp;ldquo;Verfolgung der Lebensdauer von Zeigern&amp;rdquo;, die C++ Programmierer ursprünglich in ihren Köpfen durchführten.&lt;/p>
&lt;p>Wenn man Rust lernt, nachdem man die Freiheit und die Gefahren von C++ Zeigern verstanden hat, wird man die Philosophie hinter dem Ownership-Modell und das &amp;ldquo;Warum es so entworfen wurde&amp;rdquo; tiefgründiger verstehen können.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Dieser Artikel ist eine vergleichende Betrachtung der Speicherverwaltungsmethoden in C++ und Rust. Wir hoffen, dass er als Referenz für die Auswahl der geeigneten Sprache entsprechend den Anforderungen des jeweiligen Projekts dient.&lt;/em>&lt;/p></description></item><item><title>Vorteile und Nachteile von Rust aus der Sicht eines C++-Entwicklers</title><link>http://kenji.blog/de/p/cpp-engineer-learning-rust-pros-cons/</link><pubDate>Sat, 12 Sep 2026 03:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/cpp-engineer-learning-rust-pros-cons/</guid><description>&lt;img src="http://kenji.blog/p/cpp-engineer-learning-rust-pros-cons/img/eyecatch.jpg" alt="Featured image of post Vorteile und Nachteile von Rust aus der Sicht eines C++-Entwicklers" />&lt;h1 id="einführung-eine-neue-morgenröte-in-der-systemprogrammierung">Einführung: Eine neue Morgenröte in der Systemprogrammierung
&lt;/h1>&lt;p>In der modernen Softwareentwicklung sind C++ und Rust die zwei großen Giganten, die an vorderster Front der Systemprogrammierung stehen. Viele Jahre lang herrschte C++ als absoluter König in Bereichen, in denen extreme Hardwareleistung gefordert ist, wie Betriebssysteme, eingebettete Geräte, Spiele-Engines und Hochfrequenzhandel (HFT). Ich selbst schreibe als Senior-C++-Entwickler seit der C++98-Ära mit ihrem Dschungel aus rohen Zeigern Code und habe die Welle der Modernisierung durch C++11 (Einführung von Smart Pointern, Lambda-Ausdrücken und &lt;code>auto&lt;/code>) sowie die kontinuierliche Vergrößerung der Spezifikationen in C++14/17/20 hautnah miterlebt.&lt;/p>
&lt;p>In den letzten Jahren hat Rust jedoch als Lösung für die strukturellen Probleme von C++ – insbesondere das &amp;ldquo;Fehlen von Speichersicherheit&amp;rdquo;, das zu Sicherheitslücken führt (etwa 70% der CVEs sollen speicherbedingt sein), und die &amp;ldquo;endlos komplexen Spezifikationen sowie undefiniertes Verhalten (UB)&amp;rdquo; – einen dramatischen Aufstieg erlebt. Die offizielle Einführung in den Linux-Kernel sowie groß angelegte Migrationsprojekte zu Rust durch Tech-Giganten wie Microsoft, Google und AWS sind nicht nur ein vorübergehender Trend, sondern bedeuten einen Paradigmenwechsel in der Systemprogrammierung.&lt;/p>
&lt;p>In diesem Artikel werde ich die &amp;ldquo;Vorteile&amp;rdquo; und &amp;ldquo;Nachteile&amp;rdquo;, die ich als waschechter C++-Entwickler beim tiefgreifenden Erlernen und praktischen Einsatz von Rust erfahren habe, aus einer technischen Perspektive, die an die Grundlagen des Sprachdesigns rührt, gründlich vergleichen und erläutern.&lt;/p>
&lt;hr>
&lt;h1 id="1-paradigmenwechsel-in-der-speicherverwaltung-von-raii-zu-ownership-und-borrowing">1. Paradigmenwechsel in der Speicherverwaltung: Von RAII zu Ownership und Borrowing
&lt;/h1>&lt;h2 id="die-grenzen-von-raii-und-smart-pointern-in-c">Die Grenzen von RAII und Smart Pointern in C++
&lt;/h2>&lt;p>Eine der größten Erfindungen von C++ ist &lt;strong>RAII (Resource Acquisition Is Initialization)&lt;/strong>. Dieses Konzept, bei dem Ressourcen im Konstruktor gesichert und beim Verlassen des Gültigkeitsbereichs (Scope) im Destruktor automatisch freigegeben werden, befreite Entwickler von der Angst vor Speicherlecks durch manuelles &lt;code>new&lt;/code> und &lt;code>delete&lt;/code>. Seit C++11 wurden &lt;code>std::unique_ptr&lt;/code> und &lt;code>std::shared_ptr&lt;/code> in die Standardbibliothek eingeführt, wodurch das Konzept der Eigentümerschaft (Ownership) im Code darstellbar wurde.&lt;/p>
&lt;p>Allerdings haben die Smart Pointer und die Move-Semantik von C++ eine fatale Schwäche: Die statische Überprüfung durch den Compiler ist unvollständig.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">consume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Consuming: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">ptr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">my_ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello, C++&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Eigentümerschaft an die Funktion übertragen (Move)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">consume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">my_ptr&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Gefahr: In C++ führt der Zugriff auf das Objekt nach dem Move nicht zu einem Kompilierungsfehler.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// std::move ist nur ein Cast zu einer R-Value-Referenz (T&amp;amp;&amp;amp;) und der Compiler blockiert die Nutzung nicht.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">my_ptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Pointer is still valid?&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Pointer is null.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// std::cout &amp;lt;&amp;lt; *my_ptr &amp;lt;&amp;lt; std::endl; // Undefiniertes Verhalten durch Nutzung nach Freigabe (Use-After-Free)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In C++ besteht immer das Risiko, versehentlich auf ein Objekt zuzugreifen, das durch &lt;code>std::move&lt;/code> entleert wurde (ein gültiger, aber unspezifizierter Zustand). Dies führt unmittelbar zu Abstürzen zur Laufzeit oder im schlimmsten Fall zu Sicherheitslücken.&lt;/p>
&lt;h2 id="rusts-eigentümerschaft-ownership-und-die-absolute-verteidigung-des-borrow-checkers">Rusts Eigentümerschaft (Ownership) und die absolute Verteidigung des Borrow Checkers
&lt;/h2>&lt;p>Rust hat dieses Konzept der &amp;ldquo;Eigentümerschaft&amp;rdquo; in das Kerndesign der Sprache integriert und führt durch eine Compiler-Funktion namens &lt;strong>Borrow Checker&lt;/strong> eine strenge statische Analyse durch.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fn&lt;/span> &lt;span class="nf">consume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>: &lt;span class="nb">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Consuming: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Hier verlässt s den Scope und der Speicher wird freigegeben (Drop)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">String&lt;/span>::&lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello, Rust&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Eigentümerschaft wird an die Funktion gemovet. In Rust ist Move-Semantik der Standard.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">consume&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">my_string&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Kompilierungsfehler! Auf eine Variable nach einem Move kann unter keinen Umständen mehr zugegriffen werden
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// println!(&amp;#34;Is it still there? {}&amp;#34;, my_string);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Wenn in Rust die Eigentümerschaft einer Variablen verschoben wird, behandelt der Compiler die ursprüngliche Variable so, als wäre sie &amp;ldquo;uninitialisiert&amp;rdquo;, und blockiert jeglichen weiteren Zugriff vollständig. Dadurch können Fehler wie &amp;ldquo;Use-After-Free&amp;rdquo; oder &amp;ldquo;Dangling Pointers&amp;rdquo; theoretisch nicht durch die Kompilierung gelangen.&lt;/p>
&lt;div class="mermaid">graph TD
A["C++ std::unique_ptr"] --> B["std::move angewendet"]
B --> C["Eigentümerschaft übertragen"]
C --> D["Alter Zeiger weiterhin zugänglich"]
D --> E["Potenzielles undefiniertes Verhalten (UB)"]
F["Rust Box / String"] --> G["Als Wert übergeben (Move)"]
G --> H["Eigentümerschaft übertragen"]
H --> I["Compiler blockiert alte Variable"]
I --> J["Speichersicherheit garantiert"]&lt;/div>
&lt;h2 id="ausleihen-borrowing-und-die-kontrolle-der-veränderbarkeit">Ausleihen (Borrowing) und die Kontrolle der Veränderbarkeit
&lt;/h2>&lt;p>Noch leistungsfähiger sind die Regeln für das &amp;ldquo;Ausleihen (Borrowing)&amp;rdquo;, bei dem eine Referenz auf eine Ressource erstellt wird. In Rust werden folgende Regeln erzwungen:&lt;/p>
&lt;ol>
&lt;li>Zu einem beliebigen Zeitpunkt darf &lt;strong>entweder&lt;/strong> &amp;ldquo;mehrere unveränderliche Referenzen (&lt;code>&amp;amp;T&lt;/code>)&amp;rdquo; &lt;strong>oder&lt;/strong> &amp;ldquo;genau eine veränderliche Referenz (&lt;code>&amp;amp;mut T&lt;/code>)&amp;rdquo; existieren, aber nicht beides gleichzeitig.&lt;/li>
&lt;li>Eine Referenz darf nicht länger leben als die Daten, auf die sie verweist (Lebensdauer-Beschränkungen / Lifetimes).&lt;/li>
&lt;/ol>
&lt;p>In C++ können problemlos mehrere veränderliche Referenzen oder Zeiger auf dasselbe Objekt erstellt werden, was zu unerwarteter Zerstörung des Zustands (wie Invalidation von Iteratoren) führen kann. Rust verhindert solche Fehler im Vorfeld, indem es die Kombination aus &amp;ldquo;Aliasing&amp;rdquo; und &amp;ldquo;Mutability&amp;rdquo; (Veränderbarkeit) auf Sprachebene verbietet.&lt;/p>
&lt;hr>
&lt;h1 id="2-speicherlayout-und-der-mathematische-overhead-von-smart-pointern">2. Speicherlayout und der mathematische Overhead von Smart Pointern
&lt;/h1>&lt;p>In der Systemprogrammierung ist ein genaues Verständnis des Speicherlayouts unerlässlich. Vergleichen wir &lt;code>std::shared_ptr&lt;/code> in C++ mit &lt;code>std::rc::Rc&lt;/code> / &lt;code>std::sync::Arc&lt;/code> in Rust.&lt;/p>
&lt;p>Der C++ &lt;code>std::shared_ptr&lt;/code> verwaltet Ressourcen über Referenzzählung, verwendet aber standardmäßig threadsichere atomare Operationen (&lt;code>std::atomic&lt;/code>), um den Referenzzähler zu erhöhen oder zu verringern. Dieser Overhead im Speicher kann wie folgt formuliert werden:&lt;/p>
$$ Overhead_{C++} = sizeof(T) + sizeof(ControlBlock) $$
&lt;p>Hierbei enthält der $ControlBlock$ den &amp;ldquo;Strong Ref Count&amp;rdquo; (Starker Referenzzähler), den &amp;ldquo;Weak Ref Count&amp;rdquo; (Schwacher Referenzzähler) und einen &amp;ldquo;Custom Deleter&amp;rdquo;. Das Problem dabei ist, dass der Overhead von atomaren Anweisungen (wie Cache-Line-Sperren) bedingungslos auftritt, selbst in Situationen, in denen er nur in einem einzigen Thread verwendet wird.&lt;/p>
&lt;p>Im Gegensatz dazu trennt Rust Smart Pointer streng nach ihrem Verwendungszweck.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Für Single-Thread&lt;/strong>: &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> (Reference Counted)&lt;/li>
&lt;li>&lt;strong>Für Multi-Thread&lt;/strong>: &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code> (Atomic Reference Counted)&lt;/li>
&lt;/ul>
$$ Overhead_{Rc} = sizeof(T) + 2 \times sizeof(usize) $$
$$ Overhead_{Arc} = sizeof(T) + 2 \times sizeof(AtomicUsize) $$
&lt;p>In Rust können Sie die Strafe (Penalty) für atomare Operationen vollständig vermeiden (Zero-Cost Abstraction), wenn Sie das für Single-Thread-Anwendungen spezifische &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> verwenden. Und dank des unten beschriebenen Mechanismus zur Threadsicherheit wird das versehentliche Übergeben eines &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> an einen anderen Thread durch das Typsystem vollständig verhindert.&lt;/p>
&lt;hr>
&lt;h1 id="3-threadsicherheit-der-schock-der-fearless-concurrency">3. Threadsicherheit: Der Schock der &amp;ldquo;Fearless Concurrency&amp;rdquo;
&lt;/h1>&lt;p>Die Multithread-Programmierung in C++ ging schon immer mit der ständigen Angst vor Data Races (Datenwettläufen) und Deadlocks einher.&lt;/p>
&lt;h2 id="mutexe-in-c-und-die-gefahr-der-trennung-von-daten">Mutexe in C++ und die Gefahr der Trennung von Daten
&lt;/h2>&lt;p>Das C++ &lt;code>std::mutex&lt;/code> ist lediglich ein Werkzeug zur exklusiven Steuerung eines &amp;ldquo;bestimmten Codeblocks (Kritischer Abschnitt)&amp;rdquo;, und es gibt auf Sprachebene keine Verbindung zwischen den &amp;ldquo;zu schützenden Daten&amp;rdquo; und dem &amp;ldquo;Mutex&amp;rdquo;.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;thread&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;mutex&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">shared_data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">mutex&lt;/span> &lt;span class="n">mtx&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">worker&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Selbst wenn der Entwickler vergisst, die Sperre zu erwerben, wird die Kompilierung normal durchlaufen.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// std::lock_guard&amp;lt;std::mutex&amp;gt; lock(mtx);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">shared_data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push_back&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Fatales Data Race!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="kr">thread&lt;/span> &lt;span class="n">t1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">worker&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="kr">thread&lt;/span> &lt;span class="n">t2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">worker&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="rusts-mutex-besitzt-die-daten">Rusts Mutex &amp;ldquo;besitzt&amp;rdquo; die Daten
&lt;/h2>&lt;p>In Rust kapselt (besitzt) &lt;code>Mutex&amp;lt;T&amp;gt;&lt;/code> den zu schützenden Datentyp &lt;code>T&lt;/code> mithilfe von Generics. Um auf die Daten zuzugreifen, ist es zwingend erforderlich, &lt;code>lock()&lt;/code> aufzurufen, um ein Guard-Objekt zu erhalten. Es ist syntaktisch unmöglich, die Daten zu berühren, ohne die Sperre (Lock) zu erwerben.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">sync&lt;/span>::&lt;span class="p">{&lt;/span>&lt;span class="n">Arc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Mutex&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">thread&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Die Daten sind vollständig im Mutex gekapselt.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shared_data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Arc&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Mutex&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">Vec&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">()));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handles&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">vec!&lt;/span>&lt;span class="p">[];&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">_&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Arc (threadsicherer Referenzzähler) klonen, um ihn zwischen Threads zu teilen.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">data_clone&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Arc&lt;/span>::&lt;span class="n">clone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">shared_data&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">thread&lt;/span>::&lt;span class="n">spawn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">move&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">||&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Ohne den Erwerb des Locks kann auf den internen Vec nicht zugegriffen werden.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">data_clone&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">lock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handles&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">handles&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">unwrap&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Darüber hinaus gibt es in Rust zwei zentrale Traits, die die Sicherheit der Nebenläufigkeit (Concurrency) garantieren.&lt;/p>
&lt;ul>
&lt;li>&lt;code>Send&lt;/code>: Typen, deren Eigentümerschaft sicher zwischen Threads übertragen werden kann.&lt;/li>
&lt;li>&lt;code>Sync&lt;/code>: Typen, die sicher von mehreren Threads gleichzeitig referenziert werden können.&lt;/li>
&lt;/ul>
&lt;p>Zum Beispiel implementiert das nicht-threadsichere &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> den &lt;code>Send&lt;/code>-Trait nicht. Wenn man also versucht, es an &lt;code>thread::spawn&lt;/code> zu übergeben, führt dies sofort zu einem Kompilierungsfehler. Durch diese &amp;ldquo;Fearless Concurrency (Nebenläufigkeit ohne Furcht)&amp;rdquo; werden Entwickler von der Angst vor Bugs befreit und können Parallelisierung deutlich aggressiver vorantreiben.&lt;/p>
&lt;p>Nach dem Amdahlschen Gesetz (Amdahl&amp;rsquo;s Law) kann der theoretisch maximale Durchsatz für einen parallelisierbaren Anteil $P$ und einen Parallelitätsgrad $N$ wie folgt ausgedrückt werden:&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$
&lt;p>Rust ermöglicht es, Refactorings zur Maximierung dieses $P$-Wertes äußerst sicher durchzuführen, indem man sich voll auf das Typsystem verlässt.&lt;/p>
&lt;div class="mermaid">graph TD
A["C++ Threadsicherheit"] --> B["Verlässt sich auf das Gedächtnis des Entwicklers"]
B --> C["Mutex ist von Daten entkoppelt"]
C --> D["Hohes Risiko stiller Data Races"]
E["Rust Threadsicherheit"] --> F["Send und Sync Traits"]
F --> G["Mutex besitzt die Daten"]
G --> H["Data Races zur Kompilierzeit verhindert"]&lt;/div>
&lt;hr>
&lt;h1 id="4-fehlerbehandlung-ausnahmen-vs-algebraische-datentypen">4. Fehlerbehandlung: Ausnahmen vs. Algebraische Datentypen
&lt;/h1>&lt;p>Der Standard für die Fehlerbehandlung in C++ sind &amp;ldquo;Ausnahmen (Exceptions)&amp;rdquo;. Ausnahmen machen den Kontrollfluss jedoch undurchsichtig und verursachen Performance-Einbußen (Stack Unwinding und Aufblähung der RTTI). In eingebetteten Systemen oder Spiele-Engines werden Ausnahmen oft vollständig deaktiviert (&lt;code>-fno-exceptions&lt;/code>), und man entscheidet sich für ein klassisches Design, das Fehlercodes zurückgibt. In C++23 wurde &lt;code>std::expected&lt;/code> eingeführt, aber es wird Zeit brauchen, bis es das gesamte Ökosystem durchdringt.&lt;/p>
&lt;p>In Rust gibt es das Konzept der Ausnahmen nicht. Fehler werden als reine &amp;ldquo;Werte&amp;rdquo; zurückgegeben und durch einen Aufzählungstyp (Algebraischer Datentyp) namens &lt;code>Result&amp;lt;T, E&amp;gt;&lt;/code> dargestellt.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">fs&lt;/span>::&lt;span class="n">File&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">io&lt;/span>::&lt;span class="p">{&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Read&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Allein durch den Rückgabetyp wird klar, dass ein IO-Fehler auftreten kann.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">read_file_content&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">path&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nb">Result&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nb">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">io&lt;/span>::&lt;span class="n">Error&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// Der ?-Operator bewirkt eine sofortige vorzeitige Rückgabe (Early Return) bei einem Fehler oder entpackt den Inhalt bei Erfolg.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">File&lt;/span>::&lt;span class="n">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">String&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">read_to_string&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="k">mut&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Ok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dieser &lt;code>?&lt;/code>-Operator ist revolutionär. Er eliminiert tiefe Verschachtelungen (die if-Anweisungs-Pyramiden), die bei der Überprüfung von Fehlercodes in C++ auftreten, und bewahrt gleichzeitig einen sauberen Code-Fluss, der dem von Ausnahmen ähnelt, während explizit angegeben wird, bei welchem Funktionsaufruf Fehler propagiert werden.&lt;/p>
&lt;hr>
&lt;h1 id="5-polymorphismus-von-virtuellen-funktionen-und-templates-zu-traits">5. Polymorphismus: Von virtuellen Funktionen und Templates zu Traits
&lt;/h1>&lt;p>Polymorphismus wird in C++ hauptsächlich durch Klassenvererbung und dynamischen Dispatch über virtuelle Funktionen (&lt;code>virtual&lt;/code>) oder statischen Dispatch über Templates (wie CRTP) erreicht.&lt;/p>
&lt;p>Beim dynamischen Dispatch wird ein Zeiger (vptr) auf eine Tabelle virtueller Funktionen (vtable) in das Objekt eingebettet, was beim Funktionsaufruf einen Overhead für die Zeigerauflösung verursacht.&lt;/p>
$$ T_{dispatch} = T_{lookup\_in\_vtable} + T_{dereference} $$
&lt;p>Rust hat die klassische objektorientierte &amp;ldquo;Klassenvererbung&amp;rdquo; verworfen und stattdessen das Konzept der &amp;ldquo;&lt;strong>Traits&lt;/strong>&amp;rdquo; eingeführt (ähnlich den Concepts in C++20, aber vielseitiger).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">trait&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Drawable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">Circle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">radius&lt;/span>: &lt;span class="kt">f64&lt;/span> &lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Drawable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Drawing a Circle of radius &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">radius&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Statischer Dispatch (Monomorphisierung / Zero-Overhead)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw_static&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nc">Drawable&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">item&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// Dynamischer Dispatch (Trait-Objekt)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw_dynamic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">item&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Drawable&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das Hauptmerkmal des dynamischen Dispatches in Rust (&lt;code>dyn Trait&lt;/code>) ist, dass er keinen vptr innerhalb der Datenstruktur hat, sondern einen &lt;strong>Fat Pointer&lt;/strong> verwendet. Ein Fat Pointer hält ein Paar bestehend aus einem &amp;ldquo;Zeiger auf die Daten&amp;rdquo; und einem &amp;ldquo;Zeiger auf die vtable&amp;rdquo;. Dies macht es extrem einfach, später Traits für Typen zu implementieren (zu erweitern), die in externen Bibliotheken definiert sind, und sie einem dynamischen Dispatch zu unterziehen.&lt;/p>
&lt;hr>
&lt;h1 id="6-paketverwaltung-und-build-systeme-die-qualen-von-cmake-und-der-segen-von-cargo">6. Paketverwaltung und Build-Systeme: Die Qualen von CMake und der Segen von Cargo
&lt;/h1>&lt;p>Eine der größten Schwächen von C++ ist das Fehlen eines Standard-Paketmanagers. Die obskure Syntax von &lt;code>CMakeLists.txt&lt;/code>, die Komplexität der Auflösung von Abhängigkeiten mit &lt;code>find_package&lt;/code> und die Unterschiede in den Bibliothekspfaden je nach Betriebssystem rauben C++-Entwicklern weiterhin enorm viel Zeit.&lt;/p>
&lt;p>Rust ist standardmäßig mit &lt;strong>Cargo&lt;/strong> ausgestattet, einem der weltbesten Paketmanager und Build-Systeme.&lt;/p>
&lt;div class="mermaid">graph TD
A["C++ Build-Umgebung"] --> B["CMakeLists.txt"]
B --> C["vcpkg / Conan Integration"]
C --> D["Makefiles / Ninja generieren"]
D --> E["Compiler (GCC/Clang/MSVC)"]
F["Rust Build-Umgebung"] --> G["Cargo.toml"]
G --> H["Abhängigkeiten von crates.io abrufen"]
H --> I["rustc (Cargo build)"]
I --> J["Ausführfertiges Binary"]&lt;/div>
&lt;p>Indem man einfach eine einzige Zeile mit dem Namen und der Version der Abhängigkeitsbibliothek (Crate) zu &lt;code>Cargo.toml&lt;/code> hinzufügt, werden die Auflösung transitiver Abhängigkeiten, das Herunterladen und der Build vollautomatisch durchgeführt. Darüber hinaus sind alle für die Entwicklung notwendigen Toolchains, wie Tests (&lt;code>cargo test&lt;/code>), Dokumentationsgenerierung (&lt;code>cargo doc&lt;/code>), statische Analyse (&lt;code>cargo clippy&lt;/code>) und Formatierer (&lt;code>cargo fmt&lt;/code>), in diesem einen Befehl integriert. Dieser Komfort hat eine solche Durchschlagskraft, dass man, wenn man ihn einmal erlebt hat, nie wieder zur Build-Umgebung von C++ zurückkehren möchte.&lt;/p>
&lt;hr>
&lt;h1 id="7-nachteile-und-die-lernkurve-beim-erlernen-von-rust">7. Nachteile und die Lernkurve beim Erlernen von Rust
&lt;/h1>&lt;p>Bisher habe ich über die Vorteile von Rust gesprochen, aber es gibt definitiv &amp;ldquo;Barrieren&amp;rdquo; und Nachteile, auf die C++-Entwickler stoßen, wenn sie Rust in der Praxis einsetzen.&lt;/p>
&lt;h2 id="1-der-harte-kampf-mit-dem-borrow-checker">1. Der harte Kampf mit dem Borrow Checker
&lt;/h2>&lt;p>Wenn man versucht, Datenstrukturen, die in C++ &amp;ldquo;irgendwie mit rohen Zeigern verbunden waren&amp;rdquo; (z. B. doppelt verkettete Listen, Graphenstrukturen oder selbstreferenzielle Strukturen), unverändert in Rust zu implementieren, wird die Kompilierung aufgrund von Eigentümerschafts- und Lebensdauerbeschränkungen fehlschlagen. Um den Borrow Checker zufriedenzustellen, muss man entweder komplexe Wrapper wie &lt;code>Rc&amp;lt;RefCell&amp;lt;T&amp;gt;&amp;gt;&lt;/code> verwenden oder das Design grundlegend überdenken, hin zu Arena-Allokatoren oder indexbasiertem Management.&lt;/p>
&lt;h2 id="2-lange-kompilierungszeiten">2. Lange Kompilierungszeiten
&lt;/h2>&lt;p>Auch C++ wird durch die Verschachtelung von Templates langsam kompiliert, aber die Kompilierungszeit von Rust (insbesondere bei einem Clean Build von Grund auf) ist keineswegs kurz. Da die leistungsstarken Optimierungs-Pässe von LLVM, die Makro-Erweiterung und die Monomorphisierung von Generics zusammenkommen, wird die Build-Zeit in großen Projekten zu einem Engpass. Während der Entwicklung ist es unerlässlich, Workarounds wie die häufige Nutzung von &lt;code>cargo check&lt;/code> anzuwenden.&lt;/p>
&lt;h2 id="3-interoperabilität-mit-c-codebasen">3. Interoperabilität mit C++-Codebasen
&lt;/h2>&lt;p>Während die Integration mit C (FFI) sehr reibungslos verläuft, ist es äußerst schwierig, bestehende, riesige C++-Codebasen (die Klassen, Templates und virtuelle Funktionen stark nutzen) direkt mit Rust zu verbinden. In den letzten Jahren haben sich Bridge-Tools wie &lt;code>cxx&lt;/code> und &lt;code>autocxx&lt;/code> weiterentwickelt, aber es gibt immer noch hohe Hürden für eine völlig nahtlose Migration.&lt;/p>
&lt;hr>
&lt;h1 id="fazit-sollten-wir-zu-rust-wechseln">Fazit: Sollten wir zu Rust wechseln?
&lt;/h1>&lt;p>C++ wird auch in Zukunft eine wichtige Rolle in der Entwicklung von Spiele-Engines und in bestehenden riesigen Infrastrukturen spielen. Die Modernisierung durch C++20/23 ist ebenfalls bemerkenswert und ermöglicht ein sichereres Schreiben von Code.&lt;/p>
&lt;p>Bei &amp;ldquo;neu gestarteten Systemprogrammierungsprojekten&amp;rdquo; fällt es mir jedoch zunehmend schwerer, &lt;strong>Gründe zu finden, sich nicht für Rust zu entscheiden&lt;/strong>. Die &amp;ldquo;Gewissheit&amp;rdquo; von Rust – dass man, sobald der Code kompiliert ist, von der Angst vor undefiniertem Verhalten und Speicherbeschädigung befreit ist und Nebenläufigkeit bei hoher Leistung sicher ausführen kann – verbessert das mentale Modell der Entwickler drastisch.&lt;/p>
&lt;p>Für einen C++-Entwickler bedeutet das Erlernen von Rust nicht nur das Auswendiglernen einer neuen Syntax, sondern ist eine großartige Erfahrung, um eine neue Perspektive auf &amp;ldquo;die sichere Verwaltung von Speicher und Threads&amp;rdquo; zu gewinnen. Ich lade Sie alle ein, den Komfort von Cargo und die Strenge des Borrow Checkers selbst zu erleben.&lt;/p></description></item></channel></rss>