How Rust’s Target-Independent Core Works: Difference between revisions
Created page with "There’s a moment every Rust developer has:
You’re writing code for a tiny embedded board, or compiling to WebAssembly, or even booting Rust on bare metal…
and you realize: “Wait. I have no OS, no allocator, no threads…
So how the hell is Rust still working?” The answer is the core crate — Rust’s target-independent standard library.
It is the smallest, most portable piece of the entire language, and honestly, one of the most beautiful pieces..." |
(No difference)
|
Latest revision as of 16:37, 15 November 2025
There’s a moment every Rust developer has: You’re writing code for a tiny embedded board, or compiling to WebAssembly, or even booting Rust on bare metal… and you realize: “Wait. I have no OS, no allocator, no threads… So how the hell is Rust still working?” The answer is the core crate — Rust’s target-independent standard library. It is the smallest, most portable piece of the entire language, and honestly, one of the most beautiful pieces of software engineering in Rust.
This article goes deep into:
- How core works without an OS
- How it avoids allocations
- How formatting, slices, iterators, and results exist without std
- How the compiler treats core specially
- Why core is the foundation for embedded, WASM, and kernels
- Architecture diagrams, code flows, internals, and real examples
And yes — fully human-written, fully technical, and emotionally honest. Why core Even Exists
Rust has three “layers” of libraries: core → alloc → std
Think of them like this:
- core — no OS, no heap, just pure language primitives.
- alloc — adds heap-backed containers, uses an allocator.
- std — adds OS features like threads, sockets, files.
core is the only layer guaranteed to work everywhere, even here: ✔ A bootloader ✔ An STM32 microcontroller ✔ A browser running WebAssembly ✔ A custom kernel ✔ A GPU shader runtime ✔ A Raspberry Pi with no Linux
Without core, Rust would be just a compiler classroom experiment. With core, Rust becomes a systems language. What’s Actually Inside core?
Here’s the funny part: You use core every day — you just call it by other names.
| Feature You Use | Actually Lives In | | ------------------------------------ | -------------------------- | | `Option<T>` | `core::option` | | `Result<T, E>` | `core::result` | | Iterators | `core::iter` | | `&[T]`, `&str` | `core::slice`, `core::str` | | Traits like `Copy`, `Clone`, `Sized` | `core::marker` | | Arithmetic ops | `core::ops` | | Pointers | `core::ptr` | | Panics | `core::panicking` | | `format_args!` | `core::fmt` |
This is shocking the first time you see it. All these everyday tools don’t require an OS or heap. A Peek at core’s File Layout
Here’s a simplified view of the internals:
core/
├── alloc/ (NOT heap allocation — core uses static alloc only) ├── array/ ├── char/ ├── cmp/ ├── convert/ ├── fmt/ <— formatting engine ├── future/ ├── hint/ ├── intrinsics/ <— compiler magic hooks ├── iter/ ├── mem/ ├── num/ ├── ops/ ├── option/ ├── panic/ ├── ptr/ ├── result/ ├── slice/ └── str/
Every part is designed to work with:
- no OS
- no heap
- no system calls
- no global allocator
- no threads
- no I/O
This is what makes core the “portable soul” of Rust. How core Works Without Allocation
Let’s take something we all use:
let s = [1, 2, 3]; s.iter().map(|x| x + 1);
How does .map() exist without allocation? Because iterators are lazy, and laziness costs zero memory. This is core’s philosophy: “Provide tools that need no heap, only the stack.”
Example: core::iter::Map is just:
pub struct Map<I, F> {
iter: I, f: F,
}
That’s it. Just two fields on the stack.
When you do: let x = arr.iter().map(|x| x + 1);
No memory allocated. No data created. You’ve just built a little struct with two fields. How core Handles Formatting Without I/O println! lives in std — it needs I/O.
But format_args! lives in core. Wait — formatting with no printing? Here’s what’s happening: format_args!("hi {}", 5)
Expands to something like: Arguments {
pieces: &["hi ", ""], args: &[5],
}
It’s just a structured description of formatting. core doesn't print anything. It just builds an IR (intermediate representation). Why?
Because the compiler uses it for:
- panic messages
- formatting inside alloc::format!
- debug derives
- errors inside no-std environments
Zero I/O. Zero heap. Zero runtime. How core’s Intrinsics Work (Compiler Magic)
Deep inside core::intrinsics are functions that only the compiler can call, for example:
fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
These become LLVM instructions: memcpy memmove abort unreachable
Example: Rust’s slice indexing is implemented using intrinsics:
- [inline]
pub unsafe fn get_unchecked(&self, index: usize) -> &T {
&*self.as_ptr().add(index)
}
And LLVM turns this into a real pointer math operation, even on WASM or embedded. How core Provides Panic Support Without std
You might think panics need:
- strings
- stdout
- printing
- unwinding
But core provides the minimal interface:
- [panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
This is why in embedded Rust you often see:
- [panic_handler]
fn panic(_info: &PanicInfo) -> ! {
cortex_m::asm::bkpt();
loop {}
}
core doesn’t care what the panic does. It just provides the structure (PanicInfo). The platform decides what to do. Architecture of core (ASCII Diagram)
┌──────────────────────────┐
│ rustc │
└─────────────┬────────────┘
│
▼
┌────────────────────────────────────┐
│ core crate │
│────────────────────────────────────│
│ primitives: Option, Result │
│ pointer types │
│ slices, str, iterators │
│ no heap, no OS │
└─────────────────┬──────────────────┘
│
┌──────────────────┼─────────────────┐
▼ ▼ ▼
embedded targets wasm32-unknown custom kernels
Real Example: Option<T> Implementation in core You use it daily.
Here’s how insanely simple it really is: pub enum Option<T> {
None, Some(T),
}
But the magic is in zero-cost optimizations. For example, Option<&T> is stored in one pointer:
- None is represented as null
- Some(ptr) is literally the pointer
No overhead. No extra bytes. No runtime work. Rust’s niche optimizations are implemented directly inside core.
Real Example: &str in core &str is two fields:
pub struct Str {
ptr: *const u8, len: usize,
}
String indexing, slicing, and UTF-8 validation all live inside core. And the compiler inlines almost everything:
let s = "hello"; let h = &s[0..1]; // core::str::slice_error checks
In optimized builds, Rust removes slice checks when it can prove safety. How core Enables no_std Ecosystems
Embedded Rust is possible because core gives developers:
- iter
- Option, Result
- pointer APIs
- formatting IR
- numeric traits
- cell types
- slices & byte manipulation
You can write a complete I2C driver, RTOS, or microkernel using only:
- ![no_std]
That line means: use core; not use std;
Everything still works — because core is all Rust really needs. Code Flow: How Your Rust Program Uses core Here’s the surprising truth:
Even when you use std, your program internally depends on core.
your code
↓
std (uses alloc)
↓
alloc (uses core)
↓
core (depends only on compiler)
↓
rustc/LLVM
↓
machine code
core is the only crate that rustc treats as a language primitive. Everything else — even std — is just a library. Why core Matters for the Future of Rust
The world is moving toward:
- WASM without JavaScript
- tiny IoT hardware
- edge compute
- unikernels
- WASI-as-the-new-OS
- Rust inside firmware + bootloaders
All of these require a language that can work with:
- No malloc
- No threads
- No filesystem
- No OS
Most languages simply cannot operate in that world. Rust can — because core exists. core is the “Rust DNA”: The minimal blueprint needed to express ownership, slices, pointers, traits, and control flow anywhere. Final Thoughts (Human, Emotional)
The core crate is one of the most beautiful parts of Rust. It represents the language distilled to its pure form — no OS, no fancy runtime, no threading model, no heap. Just the essence:
- predictable memory
- zero-cost abstractions
- safe pointers
- move semantics
- powerful algebraic types
When you look at core, you’re not just looking at Rust’s internals. You’re looking at what makes Rust portable, safe, and honestly, timeless. Every blinking LED on an STM32 board, every WASM module sent to a browser, every bootloader written in Rust — they all owe their existence to the quiet brilliance of the core crate.
It’s the foundation nobody sees, but everyone relies on. And that’s the kind of engineering that deserves respect.