<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rust Memory Model &amp; Internals on Atharva Pandey</title><link>https://atharva.page/series/rust-memory-model--internals/</link><description>Recent content in Rust Memory Model &amp; Internals on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Sat, 22 Mar 2025 11:30:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/rust-memory-model--internals/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 10: How rustc Works — From source code to binary</title><link>https://atharva.page/post/rust/rust-internals-compiler-pipeline/</link><pubDate>Sat, 22 Mar 2025 11:30:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-compiler-pipeline/</guid><description>&lt;p&gt;A junior on my team once asked why Rust compiles so slowly compared to Go. I gave the standard answer about monomorphization and LLVM, but realized I couldn&amp;rsquo;t actually explain the full pipeline. So I spent a weekend reading the rustc dev guide and poking at compiler internals. What I found was a surprisingly elegant six-stage pipeline, and understanding it changed how I think about Rust&amp;rsquo;s design tradeoffs.&lt;/p&gt;
&lt;h2 id="the-big-picture"&gt;The Big Picture&lt;/h2&gt;
&lt;p&gt;When you run &lt;code&gt;cargo build&lt;/code&gt;, your source code goes through six major stages before becoming a binary:&lt;/p&gt;</description></item><item><title>Lesson 9: Miri — Your safety net for unsafe Rust</title><link>https://atharva.page/post/rust/rust-internals-miri/</link><pubDate>Wed, 19 Mar 2025 08:45:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-miri/</guid><description>&lt;p&gt;I once shipped a Rust library with an unsafe block that worked perfectly on x86, passed all tests, and ran flawlessly in production for months. Then someone compiled it on ARM and it segfaulted immediately. The undefined behavior had been lurking the whole time — x86 just happened to tolerate the misaligned read that ARM wouldn&amp;rsquo;t. If I&amp;rsquo;d run Miri before releasing, I would have caught it in thirty seconds. Lesson learned the hard way.&lt;/p&gt;</description></item><item><title>Lesson 8: Custom Allocators — GlobalAlloc, arena allocation, and beyond</title><link>https://atharva.page/post/rust/rust-internals-allocator/</link><pubDate>Sun, 16 Mar 2025 13:10:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-allocator/</guid><description>&lt;p&gt;I was building a JSON parser that processed millions of small documents per second. Profiling showed that 40% of the time was spent in &lt;code&gt;malloc&lt;/code&gt; and &lt;code&gt;free&lt;/code&gt; — not parsing, not I/O, just allocating and deallocating tiny strings and vectors. Switched to an arena allocator that bulk-freed everything after each document, and throughput doubled. That experience taught me that the allocator isn&amp;rsquo;t just plumbing — it&amp;rsquo;s a performance lever.&lt;/p&gt;
&lt;h2 id="how-rust-allocates-memory"&gt;How Rust Allocates Memory&lt;/h2&gt;
&lt;p&gt;When you write &lt;code&gt;Box::new(42)&lt;/code&gt; or &lt;code&gt;Vec::with_capacity(100)&lt;/code&gt;, Rust asks the &lt;strong&gt;global allocator&lt;/strong&gt; for memory. By default, this is the system allocator — &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt; on Unix, &lt;code&gt;HeapAlloc&lt;/code&gt;/&lt;code&gt;HeapFree&lt;/code&gt; on Windows. It&amp;rsquo;s a general-purpose allocator designed to handle any allocation pattern reasonably well, but it&amp;rsquo;s not optimized for any specific pattern.&lt;/p&gt;</description></item><item><title>Lesson 7: Drop Order — Deterministic destruction and why it matters</title><link>https://atharva.page/post/rust/rust-internals-drop-order/</link><pubDate>Thu, 13 Mar 2025 07:20:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-drop-order/</guid><description>&lt;p&gt;I once had a deadlock in a Rust program that only manifested during shutdown. A mutex guard and a database connection were being dropped in the wrong order — the connection&amp;rsquo;s destructor tried to acquire the mutex that was already locked by the guard that hadn&amp;rsquo;t been dropped yet. Rust&amp;rsquo;s drop order is deterministic, but &amp;ldquo;deterministic&amp;rdquo; doesn&amp;rsquo;t mean &amp;ldquo;obvious.&amp;rdquo; Knowing the rules saved me hours of debugging.&lt;/p&gt;
&lt;h2 id="the-drop-trait"&gt;The Drop Trait&lt;/h2&gt;
&lt;p&gt;In Rust, cleanup logic is implemented through the &lt;code&gt;Drop&lt;/code&gt; trait. When a value goes out of scope, the compiler calls &lt;code&gt;drop()&lt;/code&gt; on it automatically. This is RAII — Resource Acquisition Is Initialization — borrowed from C++ but made more reliable by the ownership system.&lt;/p&gt;</description></item><item><title>Lesson 6: repr(C), repr(transparent), repr(packed) — Taking control of memory layout</title><link>https://atharva.page/post/rust/rust-internals-repr/</link><pubDate>Tue, 11 Mar 2025 09:55:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-repr/</guid><description>&lt;p&gt;The first time I wrote Rust FFI bindings to a C library, I defined a struct, passed it across the boundary, and got garbage data back. The C side was reading fields at fixed offsets, but Rust had silently reordered my fields for padding efficiency. Took me twenty minutes of staring at hex dumps to realize the layouts didn&amp;rsquo;t match. That&amp;rsquo;s the day I learned about &lt;code&gt;#[repr(C)]&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="default-rust-layout-reprrust"&gt;Default Rust Layout: repr(Rust)&lt;/h2&gt;
&lt;p&gt;By default, Rust structs use &lt;code&gt;repr(Rust)&lt;/code&gt; — the compiler is free to reorder fields, add padding wherever it wants, and generally optimize the layout however it sees fit. The only guarantees are:&lt;/p&gt;</description></item><item><title>Lesson 5: Fat Pointers — &amp;dyn Trait, &amp;[T], and &amp;str under the hood</title><link>https://atharva.page/post/rust/rust-internals-fat-pointers/</link><pubDate>Sun, 09 Mar 2025 16:40:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-fat-pointers/</guid><description>&lt;p&gt;I remember being confused why &lt;code&gt;&amp;amp;str&lt;/code&gt; was 16 bytes on a 64-bit system. A pointer is 8 bytes — what&amp;rsquo;s the other 8? That question sent me down a rabbit hole that fundamentally changed how I think about Rust&amp;rsquo;s type system. Turns out, some references carry extra baggage, and that baggage is the entire reason dynamically sized types work.&lt;/p&gt;
&lt;h2 id="thin-pointers-vs-fat-pointers"&gt;Thin Pointers vs Fat Pointers&lt;/h2&gt;
&lt;p&gt;Most references in Rust are &amp;ldquo;thin&amp;rdquo; — a single machine word pointing to the data:&lt;/p&gt;</description></item><item><title>Lesson 4: vtables — How dyn Trait actually works under the hood</title><link>https://atharva.page/post/rust/rust-internals-vtable/</link><pubDate>Fri, 07 Mar 2025 11:15:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-vtable/</guid><description>&lt;p&gt;I was profiling a parser once and found that a hot path using &lt;code&gt;dyn Iterator&lt;/code&gt; was 3x slower than the equivalent code using generics. The algorithm was identical. The difference? Dynamic dispatch — every method call went through a vtable lookup instead of being inlined. That day I learned to respect what &lt;code&gt;dyn&lt;/code&gt; really costs. Let&amp;rsquo;s open the hood.&lt;/p&gt;
&lt;h2 id="static-vs-dynamic-dispatch"&gt;Static vs Dynamic Dispatch&lt;/h2&gt;
&lt;p&gt;Rust gives you two ways to call methods on trait objects: static dispatch (generics) and dynamic dispatch (&lt;code&gt;dyn Trait&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>Lesson 3: Box — Heap allocation by choice</title><link>https://atharva.page/post/rust/rust-internals-box/</link><pubDate>Wed, 05 Mar 2025 08:30:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-box/</guid><description>&lt;p&gt;When I first started writing Rust, I used &lt;code&gt;Box&lt;/code&gt; everywhere. Came from a Java background where everything lives on the heap, and wrapping things in &lt;code&gt;Box&lt;/code&gt; felt natural. Took me a while to realize I was fighting the language — most of the time, you don&amp;rsquo;t need it. But when you do need &lt;code&gt;Box&lt;/code&gt;, nothing else will do.&lt;/p&gt;
&lt;h2 id="what-box-actually-is"&gt;What Box Actually Is&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; is the simplest smart pointer in Rust. It allocates a value of type &lt;code&gt;T&lt;/code&gt; on the heap and gives you ownership of that allocation through a pointer on the stack. When the &lt;code&gt;Box&lt;/code&gt; goes out of scope, it frees the heap memory. That&amp;rsquo;s it. No reference counting, no garbage collection, no magic.&lt;/p&gt;</description></item><item><title>Lesson 2: Stack vs Heap — Where your data actually lives</title><link>https://atharva.page/post/rust/rust-internals-stack-heap/</link><pubDate>Mon, 03 Mar 2025 14:45:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-stack-heap/</guid><description>&lt;p&gt;A colleague once asked me why their Rust program was ten times slower than expected. They were allocating a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; inside a tight loop — millions of heap allocations per second. Moved the vec outside the loop, pre-allocated with &lt;code&gt;with_capacity&lt;/code&gt;, and the function went from 2 seconds to 80 milliseconds. Stack vs heap isn&amp;rsquo;t an academic distinction. It&amp;rsquo;s the difference between fast code and slow code.&lt;/p&gt;
&lt;h2 id="two-memory-regions-two-personalities"&gt;Two Memory Regions, Two Personalities&lt;/h2&gt;
&lt;p&gt;Your program has (at minimum) two regions of memory to work with: the &lt;strong&gt;stack&lt;/strong&gt; and the &lt;strong&gt;heap&lt;/strong&gt;. They behave differently, perform differently, and serve different purposes. Rust makes the choice between them more explicit than most languages, which is one of the reasons it&amp;rsquo;s fast by default.&lt;/p&gt;</description></item><item><title>Lesson 1: Memory Layout — Size, alignment, and the padding you never asked for</title><link>https://atharva.page/post/rust/rust-internals-memory-layout/</link><pubDate>Sat, 01 Mar 2025 10:22:00 +0000</pubDate><guid>https://atharva.page/post/rust/rust-internals-memory-layout/</guid><description>&lt;p&gt;I spent an embarrassing amount of time debugging a networking project where my hand-crafted packet structs were mysteriously three bytes too large. Turns out the compiler was inserting padding I didn&amp;rsquo;t know about. That&amp;rsquo;s when I realized most Rust developers — myself included — treat memory layout as a black box. Let&amp;rsquo;s crack it open.&lt;/p&gt;
&lt;h2 id="why-layout-matters"&gt;Why Layout Matters&lt;/h2&gt;
&lt;p&gt;Every type in Rust has two fundamental properties: &lt;strong&gt;size&lt;/strong&gt; and &lt;strong&gt;alignment&lt;/strong&gt;. The size is how many bytes the value occupies. The alignment is which memory addresses the value is allowed to start at. These two numbers determine everything about how your data sits in memory, how much RAM your program uses, and whether your structs can talk to C code or hardware registers.&lt;/p&gt;</description></item></channel></rss>