<?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/ko/categories/rust/</link><description>Recent content in Rust on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 04:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/categories/rust/index.xml" rel="self" type="application/rss+xml"/><item><title>Rust의 '소유권'과 '차용'을 C++의 포인터와 비교하여 해설</title><link>http://kenji.blog/ko/p/rust-ownership-borrowing-cpp-pointer-comparison/</link><pubDate>Sat, 12 Sep 2026 04:00:00 +0900</pubDate><guid>http://kenji.blog/ko/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 Rust의 '소유권'과 '차용'을 C++의 포인터와 비교하여 해설" />&lt;p>현대 시스템 프로그래밍에서 성능과 메모리 안전성의 양립은 영원한 과제입니다. C++는 오랫동안 이 분야의 제왕으로 군림해 왔지만, 최근 그 위상을 위협하고 있는 것이 바로 Rust입니다. Rust의 가장 큰 특징은 가비지 컬렉션(GC) 없이 컴파일 타임에 메모리 안전성을 보장하는 &amp;lsquo;소유권(Ownership)&amp;lsquo;과 &amp;lsquo;차용(Borrowing)&amp;lsquo;이라는 개념에 있습니다.&lt;/p>
&lt;p>이 글에서는 C++의 포인터(원시 포인터, &lt;code>std::unique_ptr&lt;/code>, &lt;code>std::shared_ptr&lt;/code>)와 Rust의 소유권 모델을 자세히 비교하고, Rust의 컴파일러(보로우 체커)가 어떻게 Use-After-Free(해제 후 사용)나 데이터 경합(Data Race)을 방지하는지 코드 예제와 다이어그램을 통해 철저하게 해설합니다.&lt;/p>
&lt;h2 id="1-메모리-관리의-기초-스택과-힙">1. 메모리 관리의 기초: 스택과 힙
&lt;/h2>&lt;p>메모리 관리의 기본을 이해하기 위해, 먼저 프로그램이 메모리를 어떻게 활용하는지 되짚어 보겠습니다. 메모리 영역은 크게 &amp;lsquo;스택(Stack)&amp;lsquo;과 &amp;lsquo;힙(Heap)&amp;lsquo;으로 분류됩니다.&lt;/p>
&lt;h3 id="스택stack">스택(Stack)
&lt;/h3>&lt;p>함수 호출 시 지역 변수 등이 쌓이는 영역입니다. LIFO(후입선출) 구조를 가지며 메모리 할당 및 해제가 매우 빠릅니다. 컴파일 타임에 크기를 결정할 수 있는 데이터만 배치됩니다.&lt;/p>
&lt;h3 id="힙heap">힙(Heap)
&lt;/h3>&lt;p>실행 시 동적으로 크기가 결정되는 데이터나, 함수의 스코프를 넘어 생존해야 하는 데이터가 배치됩니다. 포인터(또는 참조)를 통해 접근됩니다.&lt;/p>
&lt;p>가비지 컬렉션을 갖지 않는 C++나 Rust에서는 힙 메모리 관리 비용을 수식으로 다음과 같이 모델링할 수 있습니다. 객체의 총 개수를 $N$, 할당에 걸리는 평균 시간을 $T_{alloc}$, 해제에 걸리는 평균 시간을 $T_{dealloc}$이라고 할 때, 메모리 관리 총 비용 $C_{memory}$는:&lt;/p>
$$ C_{memory} = \sum_{i=1}^{N} (T_{alloc, i} + T_{dealloc, i}) + O_{sync} $$
&lt;p>여기서 $O_{sync}$는 멀티스레드 환경에서의 상호 배제(뮤텍스나 원자적 연산)에 드는 오버헤드입니다. Rust는 컴파일 시점에 메모리 해제 타이밍을 결정하므로, 실행 시 가비지 컬렉션으로 인한 처리량 저하(Stop-The-World)를 0으로 만들면서 $T_{dealloc}$을 확실하고 안전한 타이밍에 실행합니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["프로그램 메모리"] --> B["스택 (빠름, 고정 크기)"]
A --> C["힙 (동적, 느림)"]
B --> D["지역 변수"]
B --> E["포인터/참조"]
C --> F["동적으로 할당된 데이터"]
E -.->|"가리킴"| F&lt;/div>
&lt;h2 id="2-c의-포인터-자유와-위험의-트레이드오프">2. C++의 포인터: 자유와 위험의 트레이드오프
&lt;/h2>&lt;p>C++에서의 메모리 관리 변천사를 살펴보겠습니다.&lt;/p>
&lt;h3 id="원시-포인터raw-pointers의-시대와-문제점">원시 포인터(Raw Pointers)의 시대와 문제점
&lt;/h3>&lt;p>C 언어에서 물려받은 원시 포인터(&lt;code>*&lt;/code>)는 궁극의 자유를 제공하지만, 동시에 다음과 같은 심각한 버그의 온상이 됩니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>메모리 누수(Memory Leak)&lt;/strong>: &lt;code>new&lt;/code>한 메모리를 &lt;code>delete&lt;/code>하는 것을 잊어버림.&lt;/li>
&lt;li>&lt;strong>댕글링 포인터(Dangling Pointer)&lt;/strong>: 메모리 해제 후(&lt;code>delete&lt;/code> 후)의 포인터에 접근함.&lt;/li>
&lt;li>&lt;strong>이중 해제(Double Free)&lt;/strong>: 동일한 메모리 영역을 2번 &lt;code>delete&lt;/code>해버림.&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++: 원시 포인터에 의한 문제의 예
&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">// ... 어떤 처리 ...
&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">// 실수로 다시 접근 (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">// C++ 컴파일러는 이를 컴파일 에러로 만들지 못함
&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">// 미정의 동작(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="raii와-스마트-포인터의-등장-c11-이후">RAII와 스마트 포인터의 등장 (C++11 이후)
&lt;/h3>&lt;p>C++11 이후, RAII (Resource Acquisition Is Initialization) 개념에 기반한 스마트 포인터가 표준화되어, 원시 포인터의 직접 사용은 권장되지 않습니다.&lt;/p>
&lt;h4 id="stdunique_ptr">&lt;code>std::unique_ptr&lt;/code>
&lt;/h4>&lt;p>소유권이 단일함을 표현하는 포인터입니다. 스코프를 벗어나면 자동으로 메모리가 해제됩니다. 복사는 불가능하며 소유권의 &amp;lsquo;이동(Move)&amp;lsquo;만 가능합니다(&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; // 컴파일 에러 (복사 불가)
&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">// 소유권 이동
&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">// C++의 약점: 이동 후의 p1은 nullptr이 되지만, 접근 자체는 컴파일 가능
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 실행 시 크래시(세그멘테이션 폴트)를 일으킴
&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>여러 포인터가 같은 객체를 공유할 수 있는 포인터입니다. 참조 카운트(Reference Counting)를 사용하여, 카운트가 0이 된 시점에 메모리를 해제합니다. 원자적(atomic)인 증감 연산이 필요하므로 약간의 성능 오버헤드(앞서 언급한 $O_{sync}$에 해당)가 발생합니다.&lt;/p>
&lt;h2 id="3-rust의-소유권ownership-패러다임-시프트">3. Rust의 소유권(Ownership): 패러다임 시프트
&lt;/h2>&lt;p>Rust는 C++의 &lt;code>std::unique_ptr&lt;/code> 개념을 언어 사양의 근간에 두고, 이를 더욱 엄격하게 만든 &amp;lsquo;소유권 모델&amp;rsquo;을 가지고 있습니다.&lt;/p>
&lt;h3 id="소유권의-3가지-규칙">소유권의 3가지 규칙
&lt;/h3>&lt;p>Rust의 소유권 시스템은 다음 3가지의 매우 단순한 규칙을 바탕으로 합니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Rust의 각각의 값은 소유자(owner)라고 불리는 변수를 가진다.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>어느 때든 소유자는 단 하나뿐이다.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>소유자가 스코프를 벗어나면 값은 파기된다.&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>Rust에서는 기본적으로 리소스가 &amp;lsquo;이동(Move)&amp;lsquo;됩니다. C++처럼 &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;/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: 소유권의 이동(무브)
&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">// 힙에 할당되는 데이터
&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">// 소유권이 s1에서 s2로 이동(무브)함
&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">// C++과 다른 가장 큰 점: 이동 후의 변수에 대한 접근은 &amp;#39;컴파일 에러&amp;#39;가 됨!
&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); // 컴파일 에러: 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>이 &amp;lsquo;이동 후의 변수를 컴파일 시점에 접근 불가능하게 만드는&amp;rsquo; 기능이야말로 Rust가 C++의 &lt;code>std::unique_ptr&lt;/code>보다 안전한 이유 중 하나입니다.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant S1 as "변수 s1"
participant Heap as "힙 메모리 ('hello')"
participant S2 as "변수 s2"
S1->>Heap: "할당 및 소유"
Note over S1,S2: "let s2 = s1;"
S1--xHeap: "소유권 상실 (무효화됨)"
S2->>Heap: "소유권 획득"&lt;/div>
&lt;h2 id="4-차용borrowing과-참조">4. 차용(Borrowing)과 참조
&lt;/h2>&lt;p>소유권을 항상 이동시키다 보면 함수에 값을 넘길 때마다 소유권을 돌려받아야 하므로 매우 불편합니다. 여기서 등장하는 것이 &amp;lsquo;차용(Borrowing)&amp;lsquo;입니다. C++의 포인터나 참조에 해당합니다.&lt;/p>
&lt;p>Rust의 차용에는 2가지 종류가 있습니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>불변 참조(Immutable Reference)&lt;/strong>: &lt;code>&amp;amp;T&lt;/code> (C++의 &lt;code>const T&amp;amp;&lt;/code>와 유사)&lt;/li>
&lt;li>&lt;strong>가변 참조(Mutable Reference)&lt;/strong>: &lt;code>&amp;amp;mut T&lt;/code> (C++의 &lt;code>T&amp;amp;&lt;/code>와 유사)&lt;/li>
&lt;/ul>
&lt;h3 id="보로우-체커borrow-checker의-냉혹한-규칙">보로우 체커(Borrow Checker)의 냉혹한 규칙
&lt;/h3>&lt;p>Rust 컴파일러에는 참조의 정당성을 검증하는 &amp;lsquo;보로우 체커&amp;rsquo;가 내장되어 있습니다. 보로우 체커는 다음의 엄격한 규칙을 강제합니다.&lt;/p>
&lt;blockquote>
&lt;p>임의의 스코프에서 다음 중 어느 하나만 존재할 수 있다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>하나의 가변 참조(&lt;code>&amp;amp;mut T&lt;/code>)&lt;/strong>&lt;/li>
&lt;li>&lt;strong>여러 개의 불변 참조(&lt;code>&amp;amp;T&lt;/code>)&lt;/strong>&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>이것은 **&amp;ldquo;Multiple Readers XOR Single Writer (MRSW)&amp;rdquo;**라고 불리는 원칙입니다. 수학의 배타적 논리합(XOR)으로 표현할 수 있으며, 상태 $S$에 대해 불변 참조의 수 $N_r$과 가변 참조의 수 $N_w$는 다음 제약을 만족해야 합니다.&lt;/p>
$$ (N_r \ge 0 \land N_w = 0) \oplus (N_r = 0 \land N_w = 1) $$
&lt;p>이 규칙을 통해, &lt;strong>데이터 경합(Data Race)을 컴파일 시점에 완전히 배제&lt;/strong>합니다. 데이터 경합은 ① 2개 이상의 포인터가 동일한 데이터에 동시 접근하고, ② 그중 적어도 하나가 쓰기를 수행하며, ③ 동기화 메커니즘이 없는 경우에 발생합니다. Rust는 ②의 조건을 컴파일 시점에 파괴함으로써 데이터 경합을 미연에 방지합니다.&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: 차용 규칙 위반으로 인한 컴파일 에러
&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">// 불변 차용 (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">// 불변 차용 (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; // 에러! 불변 차용이 존재하는데 가변 차용을 만들 수 없음
&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-이터레이터-무효화iterator-invalidation-방지">5. 이터레이터 무효화(Iterator Invalidation) 방지
&lt;/h2>&lt;p>보로우 체커의 위력이 가장 잘 발휘되는 구체적인 예로, &amp;lsquo;이터레이터 무효화&amp;rsquo;라는 고전적인 버그를 살펴보겠습니다.&lt;/p>
&lt;h3 id="c에서의-이터레이터-무효화-실행-시-크래시">C++에서의 이터레이터 무효화 (실행 시 크래시)
&lt;/h3>&lt;p>C++의 &lt;code>std::vector&lt;/code>를 루프 중에 변경하면 이면의 메모리가 재할당(Reallocation)될 가능성이 있으며, 이로 인해 참조가 댕글링 포인터로 변하게 됩니다.&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++: 이터레이터 무효화 버그
&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">// 벡터 요소에 대한 참조 획득
&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">// 요소 추가 (여기서 용량이 부족해지면 새로운 메모리 영역이 할당되고,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 이전 영역은 파기될 가능성이 있음)
&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는 이미 해제된 메모리를 가리키고 있을 수 있음! (미정의 동작)
&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="rust에-의한-컴파일-타임-방어">Rust에 의한 컴파일 타임 방어
&lt;/h3>&lt;p>완전히 동일한 로직을 Rust로 작성해 보겠습니다.&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: 이터레이터 무효화를 컴파일 시점에 방지
&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">// 불변 참조 획득 (차용 시작)
&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">// 에러! `first`가 `v`를 불변 차용하고 있는 동안에는,
&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`에 필요한 가변 차용을 수행할 수 없음.
&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>이처럼 Rust에서는 &amp;lsquo;값을 읽고 있는 도중(불변 차용 중)에 그 값을 변경하는(가변 차용하는) 것&amp;rsquo;이 컴파일러 레벨에서 금지되어 있기 때문에, Use-After-Free나 이터레이터 무효화와 같은 치명적인 버그가 컴파일 시점에 확실하게 포착됩니다.&lt;/p>
&lt;div class="mermaid">graph LR
A["변수 v (소유자)"] --> B["힙 배열 [1, 2, 3]"]
C["참조 'first' (&amp;v[0])"] -.->|"불변 차용"| B
A -->|X "가변 차용 거부됨!"| D["v.push(4)"]
style C stroke:#00FF00,stroke-width:2px
style D stroke:#FF0000,stroke-width:2px&lt;/div>
&lt;h2 id="6-rust에서의-공유-소유권-rc-와-arc">6. Rust에서의 공유 소유권: &lt;code>Rc&lt;/code> 와 &lt;code>Arc&lt;/code>
&lt;/h2>&lt;p>C++의 &lt;code>std::shared_ptr&lt;/code>에 해당하는 공유 소유권도 Rust에 마련되어 있지만, 싱글 스레드용과 멀티 스레드용으로 명확하게 타입이 나뉘어 있습니다.&lt;/p>
&lt;h3 id="싱글-스레드용-rct-reference-counted">싱글 스레드용: &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>는 스레드 안전성(thread-safe)이 없는 참조 카운트 스마트 포인터입니다. 원자적 명령을 사용하지 않고 카운트를 증감시키기 때문에 단일 스레드 내에서는 매우 빠릅니다. 하지만 이를 다른 스레드로 보내려고 하면 컴파일 에러가 발생합니다(&lt;code>Send&lt;/code> 트레이트를 구현하지 않았기 때문입니다).&lt;/p>
&lt;h3 id="멀티-스레드용-arct-atomic-reference-counted">멀티 스레드용: &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code> (Atomic Reference Counted)
&lt;/h3>&lt;p>스레드 간에 공유할 경우에는 원자적 증감을 수행하는 &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code>를 사용합니다. C++의 &lt;code>std::shared_ptr&lt;/code>와 동등한 비용이 듭니다.&lt;/p>
&lt;p>또한, C++에서는 &lt;code>std::shared_ptr&lt;/code>로 공유하고 있는 변수에 대해 여러 스레드에서 동시에 쓰기를 수행하면 데이터 경합이 발생합니다. 이를 방지하려면 &lt;code>std::mutex&lt;/code>를 수동으로 올바르게 사용해야 합니다.&lt;/p>
&lt;p>반면 Rust에서는 &lt;code>Arc&amp;lt;T&amp;gt;&lt;/code> 단독으로는 &lt;strong>내부의 데이터를 변경할 수 없습니다&lt;/strong>. 변경이 필요한 경우에는 뮤텍스인 &lt;code>Mutex&amp;lt;T&amp;gt;&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;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">// 스레드 안전한 공유와 상호 배제의 조합
&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">// C++의 std::shared_ptr&amp;lt;std::mutex&amp;gt;와 유사하지만, Mutex가 데이터를 내포하고 있음
&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">// lock()을 호출해야만 비로소 내부의 가변 참조(&amp;amp;mut i32)를 얻을 수 있음
&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">// 락의 해제는 RAII에 의해 스코프를 벗어나면 자동으로 이루어짐
&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>특기할 만한 점은, Rust의 &lt;code>Mutex&amp;lt;T&amp;gt;&lt;/code>는 단순한 락 메커니즘이 아니라 **&amp;ldquo;보호해야 할 데이터를 타입으로서 내포하고 있다&amp;rdquo;**는 것입니다. 이를 통해 &amp;lsquo;락을 거는 것을 잊고 데이터에 접근하는&amp;rsquo; 실수를 컴파일 레벨에서 완벽하게 방지할 수 있습니다. 락(&lt;code>lock()&lt;/code>)을 획득하지 않는 한 내부 데이터에 대한 접근 권한(참조)을 얻을 수 없는 구조로 되어 있습니다.&lt;/p>
&lt;h2 id="요약-컴파일러에-의한-사전-검사인가-개발자에-의한-자기-책임인가">요약: 컴파일러에 의한 &amp;lsquo;사전 검사&amp;rsquo;인가, 개발자에 의한 &amp;lsquo;자기 책임&amp;rsquo;인가
&lt;/h2>&lt;p>C++의 포인터나 스마트 포인터는 개발자에게 고도의 제어와 성능을 제공하지만, 그 올바른 사용은 개발자의 규율에 의존하고 있습니다. RAII나 &lt;code>std::unique_ptr&lt;/code>의 도입으로 C++는 극적으로 안전해졌지만, 여전히 이동 후 접근이나 이터레이터 무효화와 같은 &amp;lsquo;미정의 동작&amp;rsquo;을 언어 레벨에서 완벽하게 방지할 수는 없습니다.&lt;/p>
&lt;p>반면 Rust는 소유권(Ownership)과 차용(Borrowing)이라는 규칙을 컴파일러에 내장함으로써, 이러한 에러들을 실행 시점이 아닌 &lt;strong>컴파일 시점&lt;/strong>에 검출합니다. &amp;ldquo;컴파일이 통과되면 메모리 안전하다&amp;quot;라는 강력한 보장이야말로 Rust가 시스템 프로그래밍 분야에서 급속히 지지를 얻고 있는 가장 큰 이유입니다.&lt;/p>
&lt;p>Rust의 보로우 체커와 싸우는 것(Fight the borrow checker)은 초학자에게 큰 장벽이 되지만, 이는 본래 C++ 프로그래머가 머릿속에서 수행하던 &amp;lsquo;포인터의 생존 기간 추적&amp;rsquo;이라는 복잡한 계산을 컴파일러가 엄밀하게 대행해 주고 있는 것에 불과합니다.&lt;/p>
&lt;p>C++ 포인터의 자유로움과 위험성을 이해한 뒤에 Rust를 배우면, 소유권 모델의 배후에 있는 &amp;ldquo;왜 이런 설계가 되었는가&amp;quot;라는 철학을 더욱 깊이 이해할 수 있을 것입니다.&lt;/p>
&lt;hr>
&lt;p>&lt;em>본 글은 C++과 Rust의 메모리 관리 기법에 대한 비교 고찰입니다. 각 프로젝트의 요구 사항에 따라 적절한 언어를 선택하는 데 참고가 되길 바랍니다.&lt;/em>&lt;/p></description></item><item><title>C++ 엔지니어가 Rust를 배우고 느낀 장점과 단점</title><link>http://kenji.blog/ko/p/cpp-engineer-learning-rust-pros-cons/</link><pubDate>Sat, 12 Sep 2026 03:00:00 +0900</pubDate><guid>http://kenji.blog/ko/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 C++ 엔지니어가 Rust를 배우고 느낀 장점과 단점" />&lt;h1 id="머리말-시스템-프로그래밍의-새로운-새벽">머리말: 시스템 프로그래밍의 새로운 새벽
&lt;/h1>&lt;p>현대 소프트웨어 엔지니어링에서 C++와 Rust는 시스템 프로그래밍의 최전선에 서 있는 양대 산맥입니다. 오랫동안 C++는 운영 체제, 임베디드 장치, 게임 엔진, 고빈도 거래(HFT) 시스템 등 하드웨어의 극한 성능을 끌어내는 영역에서 절대적인 왕으로 군림해 왔습니다. 저 자신도 시니어 C++ 엔지니어로서 C++98 시절의 원시 포인터(raw pointer) 정글에서 시작해, C++11에 의한 현대화의 물결(스마트 포인터, 람다 표현식, &lt;code>auto&lt;/code> 도입), 그리고 C++14/17/20으로 이어지는 사양의 거대화와 나란히 달리며 코드를 계속 작성해 왔습니다.&lt;/p>
&lt;p>그러나 최근 C++가 안고 있는 구조적인 과제, 특히 &amp;ldquo;메모리 안전성 결여&amp;quot;로 인한 보안 취약점(CVE의 약 70%가 메모리 기인이라고 알려져 있습니다)과 &amp;ldquo;끝없이 복잡해지는 사양 및 미정의 동작(UB)&amp;ldquo;에 대한 해결책으로 Rust가 극적으로 대두되고 있습니다. Linux 커널에 공식 채택되거나 Microsoft, Google, AWS 등 거대 기술 기업의 대규모 Rust 전환 프로젝트는 단순한 일시적 유행이 아니라 시스템 프로그래밍의 패러다임 전환을 의미합니다.&lt;/p>
&lt;p>본 문서에서는 오리지널 C++ 엔지니어가 실제로 Rust를 깊이 배우고 실전에서 사용하며 느낀 &amp;lsquo;장점&amp;rsquo;과 &amp;lsquo;단점&amp;rsquo;을 언어 사양의 근간과 관련된 기술적 관점에서 철저하게 비교하고 해설합니다.&lt;/p>
&lt;hr>
&lt;h1 id="1-메모리-관리의-패러다임-전환-raii에서-소유권과-차용으로">1. 메모리 관리의 패러다임 전환: RAII에서 소유권과 차용으로
&lt;/h1>&lt;h2 id="c의-raii와-스마트-포인터의-한계">C++의 RAII와 스마트 포인터의 한계
&lt;/h2>&lt;p>C++의 가장 위대한 발명 중 하나가 **RAII (Resource Acquisition Is Initialization)**입니다. 생성자에서 리소스를 확보하고, 스코프를 벗어날 때 소멸자에서 자동으로 해제한다는 이 개념은 수동 &lt;code>new&lt;/code>와 &lt;code>delete&lt;/code>에 의한 메모리 누수 공포로부터 개발자를 해방시켰습니다. C++11부터는 &lt;code>std::unique_ptr&lt;/code>과 &lt;code>std::shared_ptr&lt;/code>이 표준 라이브러리에 도입되어, 소유권(Ownership) 개념을 코드 상에서 표현할 수 있게 되었습니다.&lt;/p>
&lt;p>그러나 C++의 스마트 포인터와 이동 의미론(Move Semantics)에는 컴파일러에 의한 정적 검증이 불완전하다는 치명적인 약점이 있습니다.&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">// 소유권을 함수로 이동(무브)한다
&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">// 위험: C++에서는 무브 이후의 객체에 대한 접근이 컴파일 에러가 되지 않는다
&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는 단순한 우측값 참조(T&amp;amp;&amp;amp;)로의 캐스트이며, 컴파일러는 사용을 차단하지 않는다
&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; // 해제 후 메모리 사용(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>C++에서는 &lt;code>std::move&lt;/code>에 의해 내용이 비워진(유효하지만 지정되지 않은 상태의) 객체에 대해 실수로 접근해버릴 위험이 항상 존재합니다. 런타임 충돌이나, 최악의 경우 보안 홀로 직결됩니다.&lt;/p>
&lt;h2 id="rust의-소유권ownership과-빌림-검사기의-절대적-방어">Rust의 소유권(Ownership)과 빌림 검사기의 절대적 방어
&lt;/h2>&lt;p>Rust는 이 &amp;lsquo;소유권&amp;rsquo;이라는 개념을 언어의 핵심 설계에 통합하고, **빌림 검사기(Borrow Checker)**라고 불리는 컴파일러 기능을 통해 엄격한 정적 분석을 수행합니다.&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">// 여기서 s가 스코프를 벗어나고, 메모리가 해제(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">// 소유권을 함수로 이동한다. Rust에서는 기본값이 무브 시맨틱스.
&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">// 컴파일 에러! 무브된 이후의 변수에는 절대 접근할 수 없다
&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>Rust에서는 변수의 소유권이 이동한 시점에 원본 변수는 컴파일러에 의해 &amp;lsquo;초기화되지 않은&amp;rsquo; 상태와 동등하게 취급되어, 이후의 접근을 완전히 차단합니다. 이로 인해 &amp;lsquo;Use-After-Free(해제 후 메모리 사용)&amp;lsquo;나 &amp;lsquo;Dangling Pointer(댕글링 포인터)&amp;lsquo;와 같은 버그는 이론상 컴파일을 통과할 수 없습니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["C++ std::unique_ptr"] --> B["std::move 적용"]
B --> C["소유권 이전됨"]
C --> D["이전 포인터 여전히 접근 가능"]
D --> E["잠재적인 미정의 동작 (UB)"]
F["Rust Box / String"] --> G["값으로 전달 (이동)"]
G --> H["소유권 이전됨"]
H --> I["컴파일러가 이전 변수 차단"]
I --> J["메모리 안전성 보장"]&lt;/div>
&lt;h2 id="차용borrowing과-가변성의-제어">차용(Borrowing)과 가변성의 제어
&lt;/h2>&lt;p>더욱 강력한 것은 리소스를 참조하는 &amp;lsquo;차용(Borrowing)&amp;rsquo; 규칙입니다. Rust에서는 다음 규칙이 강제됩니다:&lt;/p>
&lt;ol>
&lt;li>임의의 타이밍에 &amp;ldquo;여러 개의 불변 참조(&lt;code>&amp;amp;T&lt;/code>)&amp;rdquo; 또는 &amp;ldquo;단일 가변 참조(&lt;code>&amp;amp;mut T&lt;/code>)&amp;rdquo; 중 &lt;strong>어느 한쪽만&lt;/strong> 존재할 수 있다.&lt;/li>
&lt;li>참조는 원본 데이터의 스코프보다 오래 살아남아서는 안 된다(수명 제약).&lt;/li>
&lt;/ol>
&lt;p>C++에서는 같은 객체에 대해 여러 개의 가변적인(Mutable) 참조나 포인터를 쉽게 만들 수 있으며, 이것이 예기치 않은 상태 파괴(반복자 무효화 등)를 일으킵니다. Rust는 이 &amp;ldquo;에일리어싱(Aliasing) + 가변성(Mutability)&amp;rdquo; 조합을 언어 수준에서 금지함으로써 버그를 미연에 방지합니다.&lt;/p>
&lt;hr>
&lt;h1 id="2-메모리-레이아웃과-스마트-포인터의-수학적-오버헤드">2. 메모리 레이아웃과 스마트 포인터의 수학적 오버헤드
&lt;/h1>&lt;p>시스템 프로그래밍에서 메모리 레이아웃에 대한 정확한 이해는 필수적입니다. C++의 &lt;code>std::shared_ptr&lt;/code>과 Rust의 &lt;code>std::rc::Rc&lt;/code> / &lt;code>std::sync::Arc&lt;/code>를 비교해 봅시다.&lt;/p>
&lt;p>C++의 &lt;code>std::shared_ptr&lt;/code>은 참조 카운트를 통해 리소스를 관리하지만, 기본적으로 스레드 안전한 원자적 연산(&lt;code>std::atomic&lt;/code>)을 사용하여 참조 카운트를 증감시킵니다. 그 메모리 상의 오버헤드는 다음과 같이 공식화할 수 있습니다.&lt;/p>
$$ Overhead_{C++} = sizeof(T) + sizeof(ControlBlock) $$
&lt;p>여기서, $ControlBlock$ 에는 &amp;ldquo;강한 참조 카운터(Strong Ref Count)&amp;rdquo;, &amp;ldquo;약한 참조 카운터(Weak Ref Count)&amp;rdquo;, 그리고 &amp;ldquo;사용자 지정 소멸자(Custom Deleter)&amp;ldquo;가 포함됩니다. 문제는 단일 스레드에서만 사용하는 상황에서도 원자적 명령의 오버헤드(캐시 라인 잠금 등)가 무조건 발생한다는 점입니다.&lt;/p>
&lt;p>대조적으로, Rust는 용도에 따라 스마트 포인터를 엄격하게 분리하고 있습니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>단일 스레드용&lt;/strong>: &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code> (Reference Counted)&lt;/li>
&lt;li>&lt;strong>멀티 스레드용&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>Rust에서는 단일 스레드 전용인 &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code>를 사용하면 원자적 연산의 패널티를 완전히 피할 수 있습니다(제로 코스트 추상화). 그리고 후술할 스레드 안전성 메커니즘을 통해 &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code>를 실수로 다른 스레드에 전달하는 것은 타입 시스템에 의해 완전히 방지됩니다.&lt;/p>
&lt;hr>
&lt;h1 id="3-스레드-안전성-fearless-concurrency의-충격">3. 스레드 안전성: &amp;ldquo;Fearless Concurrency&amp;quot;의 충격
&lt;/h1>&lt;p>C++에서의 멀티 스레드 프로그래밍은 항상 데이터 레이스와 교착 상태(Deadlock)의 공포와 맞닿아 있었습니다.&lt;/p>
&lt;h2 id="c의-뮤텍스와-데이터-분리의-위험성">C++의 뮤텍스와 데이터 분리의 위험성
&lt;/h2>&lt;p>C++의 &lt;code>std::mutex&lt;/code>는 어디까지나 &amp;ldquo;특정 코드 블록(크리티컬 섹션)&amp;ldquo;을 배타적으로 제어하는 것이며, &amp;ldquo;보호해야 할 데이터&amp;quot;와 &amp;ldquo;뮤텍스&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">// 개발자가 잠금을 획득하는 것을 잊어도, 컴파일은 정상적으로 통과해버린다
&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">// 치명적인 데이터 레이스!
&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="rust의-mutex는-데이터를-소유한다">Rust의 Mutex는 데이터를 &amp;ldquo;소유&amp;quot;한다
&lt;/h2>&lt;p>Rust에서 &lt;code>Mutex&amp;lt;T&amp;gt;&lt;/code>는 제네릭스를 사용하여 보호 대상인 데이터 타입 &lt;code>T&lt;/code>를 **내포(소유)**합니다. 데이터에 접근하기 위해서는 반드시 &lt;code>lock()&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;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">// 데이터는 Mutex 안에 완전히 캡슐화된다
&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(스레드 안전한 참조 카운트)를 클론
&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">// 잠금을 획득하지 않으면, 내부의 Vec에 접근할 수 없다
&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>더 나아가 Rust에는 동시성의 안전성을 보장하는 두 가지 핵심 트레이트(Trait)가 존재합니다.&lt;/p>
&lt;ul>
&lt;li>&lt;code>Send&lt;/code>: 스레드 간에 소유권을 안전하게 전송할 수 있는 타입&lt;/li>
&lt;li>&lt;code>Sync&lt;/code>: 여러 스레드에서 동시에 참조해도 안전한 타입&lt;/li>
&lt;/ul>
&lt;p>예를 들어, 스레드 안전하지 않은 &lt;code>Rc&amp;lt;T&amp;gt;&lt;/code>는 &lt;code>Send&lt;/code> 트레이트를 구현하지 않습니다. 따라서 &lt;code>thread::spawn&lt;/code>에 전달하려고 하면 즉시 컴파일 에러가 발생합니다. 이러한 &amp;ldquo;Fearless Concurrency(두려움 없는 동시성)&amp;rdquo; 덕분에 개발자는 버그의 공포로부터 해방되어 더욱 적극적으로 병렬화를 추진할 수 있습니다.&lt;/p>
&lt;p>암달의 법칙(Amdahl&amp;rsquo;s Law)에 따르면, 병렬화 가능한 부분 $P$와 병렬도 $N$에서의 이론상 최대 처리량(Throughput)은 다음과 같이 표현됩니다.&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$
&lt;p>Rust는 이 $P$를 극대화하기 위한 리팩터링을 타입 시스템에 의존하여 극도로 안전하게 수행할 수 있게 해줍니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["C++ 스레드 안전성"] --> B["개발자의 기억력에 의존"]
B --> C["데이터와 분리된 Mutex"]
C --> D["조용한 데이터 레이스 위험성 높음"]
E["Rust 스레드 안전성"] --> F["Send 및 Sync 트레이트"]
F --> G["Mutex가 데이터를 소유함"]
G --> H["컴파일 타임에 데이터 레이스 방지"]&lt;/div>
&lt;hr>
&lt;h1 id="4-에러-처리-예외-vs-대수적-데이터-타입">4. 에러 처리: 예외 vs 대수적 데이터 타입
&lt;/h1>&lt;p>C++ 에러 처리의 표준은 &amp;lsquo;예외(Exceptions)&amp;lsquo;입니다. 그러나 예외는 제어 흐름을 불투명하게 만들고 성능 상의 페널티(스택 언와인딩 및 RTTI의 비대화)를 초래합니다. 임베디드 시스템이나 게임 엔진에서는 예외를 완전히 비활성화(&lt;code>-fno-exceptions&lt;/code>)하고 고전적인 에러 코드를 반환하는 설계를 채택하는 경우가 많습니다. C++23에서는 &lt;code>std::expected&lt;/code>가 도입되었지만, 생태계 전체로 스며드는 데는 시간이 걸릴 것입니다.&lt;/p>
&lt;p>Rust에는 예외라는 개념이 존재하지 않습니다. 에러는 순수한 &amp;lsquo;값&amp;rsquo;으로 반환되며, &lt;code>Result&amp;lt;T, E&amp;gt;&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;/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">// 반환 타입을 보는 것만으로도 IO 에러가 발생할 수 있음이 명확함
&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">// ? 연산자로 에러 시 즉시 조기 반환, 성공 시 내용 추출
&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>이 &lt;code>?&lt;/code> 연산자는 혁명적입니다. C++에서 에러 코드를 확인할 때 발생하는 깊은 중첩(if문 피라미드)을 제거하고, 예외와 같은 깔끔한 코드 흐름을 유지하면서 어떤 함수 호출에서 에러가 전파되는지를 명시적으로 기술할 수 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="5-다형성-가상-함수템플릿에서-트레이트로">5. 다형성: 가상 함수/템플릿에서 트레이트로
&lt;/h1>&lt;p>C++의 다형성은 주로 클래스 상속과 가상 함수(&lt;code>virtual&lt;/code>)를 통한 동적 디스패치(Dynamic Dispatch), 또는 템플릿에 의한 정적 디스패치(CRTP 등)로 구현됩니다.&lt;/p>
&lt;p>동적 디스패치에서는 객체에 가상 함수 테이블(vtable)에 대한 포인터(vptr)가 내장되며, 함수 호출 시 포인터를 해석하는 오버헤드가 발생합니다.&lt;/p>
$$ T_{dispatch} = T_{lookup\_in\_vtable} + T_{dereference} $$
&lt;p>Rust는 고전적인 객체 지향의 &amp;ldquo;클래스 상속&amp;quot;을 버리고, 대신 &amp;ldquo;&lt;strong>트레이트(Traits)&lt;/strong>&amp;ldquo;라는 개념을 채택했습니다(C++20의 Concept과 비슷하지만, 기능이 더 다양합니다).&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">// 정적 디스패치 (단형화/모노모피제이션・제로 오버헤드)
&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">// 동적 디스패치 (트레이트 객체)
&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>Rust 동적 디스패치(&lt;code>dyn Trait&lt;/code>)의 가장 큰 특징은 데이터 구조 내에 vptr을 가지지 않고, **팻 포인터(Fat Pointer)**를 사용한다는 점입니다. 팻 포인터는 &amp;ldquo;데이터를 가리키는 포인터&amp;quot;와 &amp;ldquo;vtable을 가리키는 포인터&amp;quot;를 쌍으로 유지합니다. 이를 통해 외부 라이브러리에 정의된 타입에 대해 나중에 트레이트를 구현(확장)하여 동적 디스패치를 적용하는 것이 매우 쉬워집니다.&lt;/p>
&lt;hr>
&lt;h1 id="6-패키지-관리-및-빌드-시스템-cmake의-고뇌와-cargo의-은혜">6. 패키지 관리 및 빌드 시스템: CMake의 고뇌와 Cargo의 은혜
&lt;/h1>&lt;p>C++의 가장 큰 약점 중 하나가 표준 패키지 매니저의 부재입니다. &lt;code>CMakeLists.txt&lt;/code>의 난해한 문법, &lt;code>find_package&lt;/code>에 의한 의존성 해결의 복잡성, OS마다 다른 라이브러리 경로 등은 C++ 엔지니어의 엄청난 시간을 빼앗아 왔습니다.&lt;/p>
&lt;p>Rust에는 &lt;strong>Cargo&lt;/strong>라는 세계 최고 수준의 패키지 매니저 겸 빌드 시스템이 기본으로 탑재되어 있습니다.&lt;/p>
&lt;div class="mermaid">graph TD
A["C++ 빌드 환경"] --> B["CMakeLists.txt"]
B --> C["vcpkg / Conan 통합"]
C --> D["Makefiles / Ninja 생성"]
D --> E["컴파일러 (GCC/Clang/MSVC)"]
F["Rust 빌드 환경"] --> G["Cargo.toml"]
G --> H["crates.io에서 의존성 가져오기"]
H --> I["rustc (Cargo 빌드)"]
I --> J["실행 가능한 바이너리"]&lt;/div>
&lt;p>&lt;code>Cargo.toml&lt;/code>에 의존 라이브러리(크레이트)의 이름과 버전을 한 줄 추가하는 것만으로 전이적 의존성 해결, 다운로드, 빌드까지 전자동으로 수행해 줍니다. 또한 테스트(&lt;code>cargo test&lt;/code>), 문서 생성(&lt;code>cargo doc&lt;/code>), 정적 분석(&lt;code>cargo clippy&lt;/code>), 포매터(&lt;code>cargo fmt&lt;/code>) 등 개발에 필요한 툴체인이 모두 이 명령어 하나에 통합되어 있습니다. 이 쾌적함은 한 번 맛보면 C++ 빌드 환경으로 돌아가고 싶지 않을 정도의 파괴력을 가지고 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="7-rust를-배우는-데-있어서의-단점과-학습-곡선">7. Rust를 배우는 데 있어서의 단점과 학습 곡선
&lt;/h1>&lt;p>지금까지 Rust의 장점을 이야기했지만, C++ 엔지니어가 Rust를 실전에 투입할 때 직면하는 &amp;lsquo;벽&amp;rsquo;이나 단점도 분명히 존재합니다.&lt;/p>
&lt;h2 id="1-가혹한-빌림-검사기와의-격투">1. 가혹한 빌림 검사기와의 격투
&lt;/h2>&lt;p>C++에서 &amp;ldquo;대충 원시 포인터로 연결해두었던&amp;rdquo; 데이터 구조(예: 이중 연결 리스트나 그래프 구조, 자기 참조 구조체 등)를 Rust에서 그대로 구현하려고 하면, 소유권과 수명 제약으로 인해 컴파일이 통과되지 않습니다. 빌림 검사기를 만족시키기 위해서는 &lt;code>Rc&amp;lt;RefCell&amp;lt;T&amp;gt;&amp;gt;&lt;/code>와 같은 복잡한 래퍼를 사용하거나 아레나 할당기(Arena Allocator), 인덱스 기반 관리로 설계를 근본적으로 재검토해야 합니다.&lt;/p>
&lt;h2 id="2-긴-컴파일-시간">2. 긴 컴파일 시간
&lt;/h2>&lt;p>C++도 템플릿의 중첩으로 인해 컴파일이 느려지지만, Rust의 컴파일 시간(특히 제로에서 시작하는 클린 빌드)도 결코 짧지 않습니다. LLVM의 강력한 최적화 패스, 매크로 확장, 제네릭스의 단형화(모노모피제이션)가 겹치기 때문에 대규모 프로젝트에서는 빌드 시간이 병목이 됩니다. 개발 중에는 &lt;code>cargo check&lt;/code>를 자주 사용하는 등의 연구가 필수적입니다.&lt;/p>
&lt;h2 id="3-c-코드-베이스와의-상호-운용성">3. C++ 코드 베이스와의 상호 운용성
&lt;/h2>&lt;p>C 언어(FFI)와의 연동은 매우 매끄럽지만, 기존의 거대한 C++ 코드 베이스(클래스, 템플릿, 가상 함수를 다수 사용하는 것)와 Rust를 직접 연동하는 것은 매우 어렵습니다. 최근에는 &lt;code>cxx&lt;/code>나 &lt;code>autocxx&lt;/code> 같은 브리지 도구가 발전하고 있지만, 완전하고 원활한 전환에는 아직 높은 진입 장벽이 있습니다.&lt;/p>
&lt;hr>
&lt;h1 id="요약-우리는-rust로-마이그레이션해야-하는가">요약: 우리는 Rust로 마이그레이션해야 하는가?
&lt;/h1>&lt;p>C++는 앞으로도 게임 엔진 개발이나 기존의 거대한 인프라스트럭처에서 중요한 역할을 계속 담당할 것입니다. C++20/23에 의한 현대화도 눈부시며 더욱 안전하게 작성할 수 있게 되었습니다.&lt;/p>
&lt;p>그러나 &amp;ldquo;새로 시작하는 시스템 프로그래밍 프로젝트&amp;quot;에 있어서 저는 이제 &lt;strong>Rust를 선택하지 않을 이유를 찾는 것이 더 어렵다&lt;/strong>고 느낍니다. 컴파일만 통과하면 미정의 동작과 메모리 파괴의 공포에서 해방되고 높은 성능으로 안전하게 병렬 처리를 수행할 수 있다는 Rust의 &amp;ldquo;확실성&amp;quot;은 엔지니어의 멘탈 모델을 극적으로 개선합니다.&lt;/p>
&lt;p>C++ 엔지니어에게 Rust의 학습은 단순히 새로운 문법을 외우는 것이 아니라, &amp;ldquo;메모리와 스레드의 안전한 관리 방법&amp;quot;에 대한 새로운 시각을 얻는 최고의 경험입니다. 여러분도 꼭 Cargo의 쾌적함과 빌림 검사기의 엄격함을 체험해 보시기 바랍니다.&lt;/p></description></item></channel></rss>