✍️ 필사 모드: The Return of Rust and Systems Programming — Ownership, Borrowing, Lifetime, Trait, Async, Tokio, Cargo, WebAssembly (2025)
English"In Rust, we don't trust the programmer. We trust the compiler — and the programmer, trained by the compiler, becomes trustworthy." — Niko Matsakis (Rust core team)
In February 2024, the White House officially recommended migration from C/C++ to memory-safe languages. In February 2025, the Linux kernel merged Rust drivers into its main tree. Nine years at the top of Stack Overflow's "most loved language" survey. This is not fandom — it signals a structural shift across the entire software industry.
Despite Rust's reputation for difficulty, why is it this influential? This post answers what it means to "understand Rust," from its origin to the 2025 ecosystem map.
1. The Birth of Rust — Mozilla's Gift
Graydon Hoare's personal project (2006)
- A Canadian engineer frustrated by his broken apartment elevator
- Wanted "a language where software doesn't die"
- Initially an experimental language with heavy OCaml influence
Mozilla official project (2010)
- Mozilla hired Graydon and invested in Rust
- Servo — a browser engine experiment written in Rust
- 1.0 released in 2015
Servo to Firefox Quantum (2017)
- Servo's CSS/rendering engines (Stylo, WebRender) shipped in Firefox
- First large-scale production success
- 2x performance, fewer bugs
Foundation independence (2021)
- Mozilla's financial crisis dissolved the Rust team
- Rust Foundation established by AWS, Google, Microsoft, Huawei, Meta
- Distributed governance
2025 — ubiquity
- Linux kernel (Rust for Linux)
- Windows kernel (Microsoft porting)
- Browsers (Firefox, revived Servo), JS tooling (swc, oxc, Turbopack)
- Runtimes (Deno), databases (TiKV, Qdrant, SurrealDB)
- Blockchain (Solana, Polkadot)
2. Ownership — Memory Safety Without GC
The problem
- C/C++: manual management leads to use-after-free, double-free, leaks
- Java/Go: GC causes unpredictable latency and memory overhead
- Is there a third path that avoids both?
Rust's three rules
- Each value has exactly one owner
- Values are dropped when the owner goes out of scope
- Once the owner changes, the previous owner cannot be used
Basic example
fn main() {
let s = String::from("hello");
let t = s; // ownership moves from s to t
// println!("{}", s); // error! s is no longer valid
println!("{}", t); // OK
} // t goes out of scope -> drop
Copy vs Move
- Stack values (i32, bool, char): Copy
- Heap-allocated (String, Vec): Move
let x = 5;
let y = x; // Copy
println!("{} {}", x, y); // OK
let s1 = String::from("hi");
let s2 = s1; // Move
// println!("{}", s1); // error!
Clone — explicit copy
let s1 = String::from("hi");
let s2 = s1.clone(); // copies the heap
println!("{} {}", s1, s2); // OK
.clone() makes the cost visible in the code. No implicit performance hit.
3. Borrowing — The Art of Lending
Why it is needed
fn print_len(s: String) { println!("{}", s.len()) }
let s = String::from("hi");
print_len(s); // ownership moved
// s cannot be used here
Inconvenient. Hence references &.
Immutable borrow
fn print_len(s: &String) { println!("{}", s.len()) }
let s = String::from("hi");
print_len(&s);
println!("{}", s); // OK, ownership kept
Mutable borrow
fn add_world(s: &mut String) {
s.push_str(" world")
}
let mut s = String::from("hi");
add_world(&mut s);
println!("{}", s); // "hi world"
Core rule — "shared XOR mutable"
At any moment:
- N immutable borrows OR
- 1 mutable borrow
- Never both
let mut s = String::from("hi");
let r1 = &s;
let r2 = &s;
let r3 = &mut s; // error! immutable borrows already exist
Why this rule
- Data races become impossible — what is shared cannot mutate
- Proven at compile time — zero runtime overhead
- Foundation of Rust's "fearless concurrency"
4. Lifetime — How Long a Reference Lives
The problem
fn dangling() -> &String {
let s = String::from("hi");
&s
} // s is dropped; &s would dangle
Segfault in C, compile error in Rust.
Lifetime annotations
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
'a is a shared lifetime inside the function. The return value lives as long as the shorter of x and y.
Lifetime elision
Most cases do not require annotation:
fn first_word(s: &str) -> &str { /* ... */ }
// internally: fn first_word<'a>(s: &'a str) -> &'a str
Three rules:
- Each reference parameter gets its own lifetime
- With one parameter, the return lifetime equals that parameter
- With
&self, the return lifetime equals self's
'static
let s: &'static str = "hello"; // string literal
Lives for the entire program. Often abused.
Why lifetime is hard
- Early on, "why do I even need this?"
- Tough cases (self-referential structs)
- Solution: do not write lifetimes upfront — learn them when the compiler asks
5. Trait System — Rust's Core Abstraction
Trait equals Interface plus Mixin plus Typeclass
trait Greet {
fn hello(&self) -> String;
fn goodbye(&self) -> String { // default
String::from("bye")
}
}
struct Kim { name: String }
impl Greet for Kim {
fn hello(&self) -> String {
format!("Hi, I am {}", self.name)
}
}
Generic with trait bounds
fn greet_all<T: Greet>(people: &[T]) {
for p in people {
println!("{}", p.hello());
}
}
T: Greet means T is any type that implements Greet. Similar to Java generics upper bounds but more powerful.
impl Trait (2018)
fn make_greeter() -> impl Greet {
Kim { name: String::from("Kim") }
}
"Returns some Greet implementor" — hides the concrete type.
Trait objects — dyn Trait
fn greet_all(people: &[Box<dyn Greet>]) {
for p in people {
println!("{}", p.hello());
}
}
vtable-based dynamic dispatch. Similar to C++ virtual functions, but the dyn keyword makes the cost explicit.
Orphan rule
You can only impl when either the trait or the type is owned by the current crate. Prevents conflicting implementations from different crates.
Famous traits
Debug—{:?}outputClone,Copy— copyingIterator— for loopsDrop— destructorFrom/Into— conversionsDeref—*overload
6. Zero-cost Abstractions — Bjarne's Promise, Rust's Delivery
Philosophy
"What you don't use, you don't pay for. What you do use, you couldn't hand-code better." — Bjarne Stroustrup
Example 1 — Iterator
let sum: i32 = (1..=100)
.filter(|n| n % 2 == 0)
.map(|n| n * 2)
.sum();
High-level functional chain compiles to assembly identical to a hand-written for-loop. LLVM optimization erases the abstraction entirely.
Example 2 — Option<T>
let x: Option<i32> = Some(5);
let y = x.unwrap_or(0);
Safer than C null checks but has zero runtime cost. Option compiles to a tagged union.
Example 3 — Generic vs dyn Trait
// Generic — monomorphized at compile time
fn static_greet<T: Greet>(x: T) { /* ... */ } // inlinable
// Trait object — runtime vtable
fn dynamic_greet(x: &dyn Greet) { /* ... */ } // pointer indirection
You choose the cost.
7. Error Handling — Result, Not panic
Result<T, E>
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("zero division"))
} else {
Ok(a / b)
}
}
match divide(10, 2) {
Ok(v) => println!("{}", v),
Err(e) => eprintln!("{}", e),
}
The ? operator — error propagation
fn do_stuff() -> Result<(), MyError> {
let data = read_file("x.txt")?;
let parsed = parse(&data)?;
save(parsed)?;
Ok(())
}
Clean propagation without try/catch. The caller must return Result.
panic! — only for exceptional cases
- Out-of-bounds, unwrap failures
- Unrecoverable logic errors
- Should almost never fire in production
thiserror and anyhow
thiserror— library-side, explicit error enumsanyhow— application-side, ergonomic composition
use anyhow::{Result, Context};
fn load_config() -> Result<Config> {
let s = std::fs::read_to_string("config.yml")
.context("failed to read config.yml")?;
Ok(serde_yaml::from_str(&s)?)
}
8. Async/Await and Tokio
async/await basics
async fn fetch_user(id: u64) -> Result<User> {
let resp = reqwest::get(format!("/users/{}", id)).await?;
Ok(resp.json().await?)
}
#[tokio::main]
async fn main() {
let user = fetch_user(1).await.unwrap();
println!("{:?}", user);
}
async produces a Future
Calling an async fn alone does nothing — it returns a Future. A runtime must poll it to execute.
Runtimes
- Tokio — de facto standard, multi-threaded, I/O optimized
- async-std — std-style API
- smol — lightweight
- monoio / glommio — io_uring, thread-per-core
Tokio strengths
- Millions of concurrent connections
tokio::spawnfor async taskstokio::select!,join!for parallel control- Rich ecosystem (hyper, tonic, axum, tower)
Difference from JS async/await
| Aspect | JS | Rust |
|---|---|---|
| Default execution | starts immediately (Promise) | nothing happens until awaited |
| Runtime | built-in | library |
| Type | Promise<T> | impl Future<Output=T> |
| Cancellation | implicit, tricky | drop cancels automatically |
| Performance | V8 tuning limits | native |
Async trait — finally in 2024
async fn in trait required workarounds for years. Rust 1.75 (Dec 2023) began stabilizing it; finalized in 2024-2025.
9. Cargo — The Package Manager Ideal
One tool for everything
cargo new myapp # new project
cargo build # build
cargo run # run
cargo test # tests
cargo bench # benchmarks
cargo doc # docs
cargo fmt # formatting (rustfmt)
cargo clippy # lint
cargo publish # upload to crates.io
Dependencies, build, test, docs, benchmarks — one standard command each. Stark contrast to Python/Node fragmentation (pip/poetry, npm/yarn/pnpm).
Cargo.toml
[package]
name = "myapp"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
[dev-dependencies]
criterion = "0.5"
Features — optional capabilities
[features]
default = ["tokio"]
tokio = ["dep:tokio"]
async-std = ["dep:async-std"]
Supports multiple runtimes within the same crate, combined with conditional compilation.
Workspace — built-in monorepo
[workspace]
members = ["crates/*"]
Multiple crates in one repo, unified dependency versions.
crates.io
- Central registry
- 170k+ crates
cargo publishone-liner- Versions can be yanked but never deleted
10. Famous Rust Projects 2025
Infrastructure
- ripgrep — grep replacement, much faster
- fd — find replacement
- bat — cat with syntax highlight
- exa/eza — improved ls
- starship — shell prompt
- helix — editor
Containers / cloud
- containerd plugin
- firecracker (AWS Lambda) — partially Rust
- Linkerd2-proxy — service mesh proxy
- kube-rs — Rust K8s client
Frontend tooling
- swc — Babel replacement
- Turbopack — Webpack successor
- Rspack — Webpack-compatible
- Biome (fka Rome) — lint+format
- oxc — new JS toolchain
Runtime / DB
- Deno — Node alternative
- Qdrant — vector DB
- SurrealDB — multi-model
- TiKV — distributed KV
- InfluxDB 3 — time-series (rewritten in Rust)
- Neon — serverless Postgres (partial Rust)
Web / server
- axum — Tokio team web framework
- actix-web — high performance
- rocket — developer-friendly
- tower — middleware ecosystem
11. Rust on the Frontend — WebAssembly
wasm-bindgen
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
Callable from JS as greet("Kim").
Leptos — Solid.js style
#[component]
fn Counter() -> impl IntoView {
let (n, set_n) = create_signal(0);
view! {
<button on:click=move |_| set_n.update(|v| *v += 1)>
"count: " {n}
</button>
}
}
- Fine-grained reactivity
- SSR + Hydration
- Very small bundles
Dioxus — React style
fn Counter(cx: Scope) -> Element {
let n = use_state(cx, || 0);
render! {
button { onclick: move |_| n.set(n + 1), "{n}" }
}
}
- React-like JSX macro
- Web, Desktop, Mobile, TUI cross-platform
- Rapid growth in 2024-2025
Yew
- Oldest Rust frontend framework
- Virtual DOM based
- Conservative, stable
Reality
- Production adoption still small
- Targeted at specific performance bottlenecks rather than full React/Vue replacement
- Used as Wasm modules in complex clients like Figma, Photopea
12. Rust for Linux — Victory in 2025
The long road
- 2020: Linus Torvalds calls it "worth considering"
- 2021: formal mailing list proposal
- 2022: initial Rust support in kernel 6.1
- 2024: some drivers (Apple GPU, parts of the open Nvidia driver)
- 2025: major driver merges
Impact
- 70% of kernel CVEs are memory-safety bugs
- Rust eliminates them at the source
- Microsoft porting parts of the Windows kernel to Rust
Tensions
- Resistance from some C maintainers ("tribalism, not code")
- A well-known maintainer resigned in 2024
- Language transition is a cultural, not only technical, problem
Asahi Linux team
- Apple Silicon Linux — nearly all new drivers in Rust
- The most radical real-world Rust deployment
13. Government and Regulation — Rust Mandated
White House ONCD report (Feb 2024)
"Memory-safe languages, including Rust, Go, C#, Java, Swift, Python, and JavaScript, offer...the most comprehensive approach to eliminate an entire class of vulnerabilities."
DoD, NASA, aviation
- Rust recommended for new safety-critical systems
- Rising support relative to C/C++ audit burdens
Ferrocene — certified Rust compiler
- ISO 26262 (automotive functional safety)
- DO-178C (avionics)
- Commercial support from Ferrous Systems
2025 today
- Infineon, Volvo, Boeing pilots
- Rust emerges as a mainstream candidate for safety-critical embedded
14. Fastest Path to Learn Rust
Stage 1 (1-2 weeks)
- Finish The Rust Book (free, official)
- 100
rustlingsexercises - Build a small CLI (argh/clap)
Stage 2 (2-4 weeks)
- Get comfortable with Ownership and Borrowing
- Internalize
Resultand? - Master Iterator chains
- Practice Trait design
Stage 3 (1-3 months)
- Network apps with Async/Tokio
- JSON/YAML with serde
- Error type design (thiserror/anyhow)
- Ship a small real project
Stage 4 (3+ months)
- Understand
unsafe - Macros (declarative / procedural)
- Advanced lifetimes
- FFI (C interop)
- Contribute to open source crates
Free resources
- The Rust Book (doc.rust-lang.org/book)
- Rust by Example
- Rustlings (github.com/rust-lang/rustlings)
- Rust for JavaScript Developers
- Zero to Production in Rust (Luca Palmieri, paid)
15. Top 10 Anti-patterns
- Over-using
.clone()— compiles, but wastes performance .unwrap()everywhere — panics in production- Avoiding borrow rules with
Rc<RefCell<T>>— giving up Rust's strengths - Async for everything — CPU-bound code does not need it
- Over-specifying lifetimes — elision handles most
dyn Traitwithout considering cost — generics often faster- Macro overuse — compile-time blowup, debugger hell
Box<dyn Error>overuse — losing fine-grained error typesunsafewhen you do not need it — forfeiting safety- Non-Rust paradigms — stick to snake_case and trait-centric design
16. Rust Wisdom Checklist
- Run
cargo clippyin CI — unify style -
cargo fmtas pre-commit — auto format -
#![deny(unsafe_code)]default in libraries - Design error types — thiserror + anyhow combo
- Public API docs —
///comments with doctests - Benchmarks — criterion to prevent regressions
- Miri testing — verify unsafe
- cargo-deny — license and vulnerability audit
- Declare MSRV — Minimum Supported Rust Version
- CI across platforms — Linux/Mac/Windows
- Design feature flags — optional deps
- Async vs sync decision — do you really need async?
Closing — "The Renaissance of Systems Programming"
For 30 years, systems programming was a C/C++ monopoly. Few believed it could be broken. Even when Rust 1.0 shipped in 2015, most experts dismissed it as a curiosity.
A decade later, the Linux kernel, the White House, Azure, and AWS are all moving to Rust. This resembles generational change more than revolution. C will not vanish, but new system code is increasingly born in Rust.
Rust's real gift is not speed. It is the experience of collaborating with the compiler to write correct programs. Fighting the borrow checker trains your intuition for memory, concurrency, and lifetimes — and that intuition travels with you into any language.
"Writing Rust is sometimes like being shouted at by an expert compiler. When you finally agree with it, your code is bulletproof." — Amos Wenger (fasterthanli.me)
"Rust is for when you need to be fast. Go is for when you need to be done. Python is for when you need both. TypeScript is for when you had a Python and want to sleep at night." — Anonymous HN comment (2024)
현재 단락 (1/346)
In February 2024, the White House officially recommended migration from C/C++ to memory-safe languag...