Skip to content

✍️ 필사 모드: The Return of Rust and Systems Programming — Ownership, Borrowing, Lifetime, Trait, Async, Tokio, Cargo, WebAssembly (2025)

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

"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

  1. Each value has exactly one owner
  2. Values are dropped when the owner goes out of scope
  3. 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:

  1. Each reference parameter gets its own lifetime
  2. With one parameter, the return lifetime equals that parameter
  3. 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{:?} output
  • Clone, Copy — copying
  • Iterator — for loops
  • Drop — destructor
  • From/Into — conversions
  • Deref* 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 enums
  • anyhow — 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::spawn for async tasks
  • tokio::select!, join! for parallel control
  • Rich ecosystem (hyper, tonic, axum, tower)

Difference from JS async/await

AspectJSRust
Default executionstarts immediately (Promise)nothing happens until awaited
Runtimebuilt-inlibrary
TypePromise<T>impl Future<Output=T>
Cancellationimplicit, trickydrop cancels automatically
PerformanceV8 tuning limitsnative

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 publish one-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)

  1. Finish The Rust Book (free, official)
  2. 100 rustlings exercises
  3. Build a small CLI (argh/clap)

Stage 2 (2-4 weeks)

  1. Get comfortable with Ownership and Borrowing
  2. Internalize Result and ?
  3. Master Iterator chains
  4. Practice Trait design

Stage 3 (1-3 months)

  1. Network apps with Async/Tokio
  2. JSON/YAML with serde
  3. Error type design (thiserror/anyhow)
  4. Ship a small real project

Stage 4 (3+ months)

  1. Understand unsafe
  2. Macros (declarative / procedural)
  3. Advanced lifetimes
  4. FFI (C interop)
  5. 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

  1. Over-using .clone() — compiles, but wastes performance
  2. .unwrap() everywhere — panics in production
  3. Avoiding borrow rules with Rc<RefCell<T>> — giving up Rust's strengths
  4. Async for everything — CPU-bound code does not need it
  5. Over-specifying lifetimes — elision handles most
  6. dyn Trait without considering cost — generics often faster
  7. Macro overuse — compile-time blowup, debugger hell
  8. Box<dyn Error> overuse — losing fine-grained error types
  9. unsafe when you do not need it — forfeiting safety
  10. Non-Rust paradigms — stick to snake_case and trait-centric design

16. Rust Wisdom Checklist

  • Run cargo clippy in CI — unify style
  • cargo fmt as 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...

작성 글자: 0원문 글자: 14,206작성 단락: 0/346